ORFinder, the easy way to abuse mail servers



Hi fellas,

Let me introduce you my last tool, ORFinder

ORFinder claims to be able to find any smtp services, exposed over the internet, vulnerable to open relay attack.
Such attack relies on the fact that the sender / receiver addresses are not checked by the underlying mail server, allowing to perform phishing attacks or any kind of fraud. Which could be valuable for any OPSEC.

DISCLAIMER: I’m not responsible for misuse or for any damage that you may cause! You agree that you use this software at your own risk.

Note: The usage of a VPN could disrupt the tool behaviors. Stay tuned for updates

How it works

Here is, in a few lines, the tool workflow:

Get IP ranges according to the given country code
For each IP
	Check if the smtp service is running
	if True
		Check if the service is vulnerable to open relay attack

Pretty straightforward, isn’t it ?



To build the container, just use this command:

docker build -t orfinder .

Docker will download the Debian image and then execute the installation steps.

Be patient, the process can be quite long the first time.


Once the build process is over, get and enjoy your new open relay scanner !

docker run -it --rm orfinder -c COUNTRY_CODE

Don’t forget to regularly pull this repository for updates.


Here is a few questions that you guys asked me on IRC.

How did you get the IP ranges of a given country ?

I advise you to check the Loader :wink: I don’t want to disclose the source here to avoid Google indexing.

How did you detect open services ?

The detection is performed through a simple TCP SYN scan.

How did you detect if a given service is vulnerable ?

Once the TCP connection established with the target, I send the following commands and check the return value.

HELO domain.com
MAIL FROM: [email protected]
RCPT TO: [email protected]

If each of those commands return 250, it means that the service is potentially vulnerable to open relay attacks. I said potentially cause, some mail servers implement security measures that cannot be detected without being, prior, root :stuck_out_tongue: .
Consequently, a manual check should be done once the vulnerable service has been detected.

Is it safe to use ?

TCP SYN scan is, probably, the stealthiest way to perform port scanning. Moreover, our requests should be drowned by the bots scanning the internet constantly.

Upstream, the vulnerability check, instantiate a TCP connection with the target. For privacy reasons, I chose to proxify each request made through TOR not to disclose the end user identity.

Last words

Contributions are welcome if you have ideas to enhance this tool :wink:

I hope you enjoyed your reading.


(The memelord of 0x00sec) #2

holy sh*t man i’ve been waiting for this! praise the lord @Nitrax and his miracles!

i am surprised at the simplicity of this tool. i’ll be sure to reincarnate parts of your code and scan for something else once i have the time! maybe something that scans iot devices…


Well, you are welcome :slight_smile:

Go ahead mate and don’t forget to pm me your repo :wink:


I looked over where you’re resolving IPs to locations, and I thought it might be helpful to have a local database of this information. Here is a short script to populate a sqlite db with ip blocks in CIDR notation. The source I found doesn’t have city or regional information, but it was pretty simple to scrape. Posting in case anyone else would find it useful.

#!/usr/bin/env python3

# Populate ip database with all country ip blocks in CIDR notation

import sqlite3
import requests
import bs4
import re

conn = sqlite3.connect('ip.db')
c = conn.cursor()

c.execute('CREATE TABLE IF NOT EXISTS blocks (country TEXT, code TEXT, block TEXT)')

r = requests.get('http://ipdeny.com/ipblocks')

soup = bs4.BeautifulSoup(r.text,'lxml')
table = soup.find('table',attrs={'cellspacing':'1'})
rows = table.find_all('tr')

for i,row in enumerate(rows):
    data = row.find_all('td')
    description = data[0].text.split()
    # find index of country code with REGEX
    code_index = 1
    for j,word in enumerate(description):
        if re.match('\([A-Z]{2}\)',word):
            code_index = j
    country = ' '.join(description[:code_index])
    code = description[code_index].strip('()') # how lewd
    link = data[1].find('a')['href']
    rblock = requests.get('http://ipdeny.com/ipblocks/'+link)
    blocks = rblock.text.split()
    for block in blocks:
        c.execute('INSERT INTO blocks (country,code,block) VALUES (?,?,?)',(country,code,block))
    print('\r{0}/{1} complete'.format((i+1),len(rows)),end='',flush=True)
print('\nCommitting changes and exiting . . .')

(Command-Line Ninja) #6

This is amazing! Have you seen my geo2ip project? That could be baller for this. I’m reading them from CSVs and never got round to populating a sqlite db with them. You should put this on github and make it a module.


Here a script from a friend that can help to find CIDR Range

[code]#!/usr/bin/env python

Country to Network CIDR Range Grabber

By: Hood3dRob1n

import math, optparse, os, sys, urllib
from colorama import Style, Fore # sudo pip install colorama

Establish Home Dir

HOME = os.path.dirname(os.path.abspath(file)) + “/”

Colors on Windows, idk if this really works or not?

if os.name == ‘nt’ or sys.platform.startswith(‘win’): colorama.init()

Some Simple Print functions

def caution( msg ): print Style.BRIGHT + Fore.YELLOW + “[” + Fore.WHITE + “" + Fore.YELLOW + "] " + Fore.WHITE + str( msg ) + Fore.RESET
def good( msg ): print Style.BRIGHT + Fore.GREEN + “[” + Fore.WHITE + "
” + Fore.GREEN + "] " + Fore.WHITE + str( msg ) + Fore.RESET
def status( msg ): print Style.BRIGHT + Fore.BLUE + “[” + Fore.WHITE + “*” + Fore.BLUE + "] " + Fore.WHITE + str( msg ) + Fore.RESET
def error( msg ): print Style.BRIGHT + Fore.RED + “[” + Fore.WHITE + “ERROR” + Fore.RED + "] " + Fore.WHITE + str( msg ) + Fore.RESET
def print_line( msg ): print Style.BRIGHT + Fore.WHITE + str( msg ) + Fore.RESET

def banner():
""“0x1337 Banner”""
print Style.BRIGHT + Fore.BLUE + “\nCountry to Network CIDR Range Grabber\nBy” + Fore.WHITE + “: Hood3dRob1n\n” + Fore.RESET

def cls():
if os.name == ‘nt’ or sys.platform.startswith(‘win’): os.system(‘cls’)
else: os.system(‘clear’)

def country_to_cidr(ccode):
Fetches the latest Country IP Allocations from RIPE Database
This link is updated daily so should be decent source for some time :slight_smile:
Full list is filtered by requested country code
Returns list array of the matching CIDR ranges for said country code
opener = urllib.FancyURLopener()
f = opener.open(“ftp://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-latest”)
for line in lines:
if ((‘ipv4’ in line) & (country_code.upper() in line)) :
cidr_range.append("%s/%d" % (net,(32-math.log(cidr,2))))
if verbose: good("%s/%d" % (net,(32-math.log(cidr,2))))
return cidr_range

Parse Arguments/Options

parser = optparse.OptionParser(banner(), version="%prog v0.02a")
parser.add_option("-c", “–country-code”, dest=“code”, default=None, type=“string”, help=“Two Letter Country Code to find CIDR Range for”)
parser.add_option("-v", action=“count”, default=0, dest=“verbose”, help=“Enable Verbose Output”)
(options, args) = parser.parse_args()

Make sure we got arguments

args = sys.argv[1:]
if not args:

if options.code == None:
print ’ '
error(“Missing Argument: --country-code\n”)

country_code = options.code.upper()
if not os.path.isfile(RESULTSDIR) and not os.path.isdir(RESULTSDIR):
os.mkdir(RESULTSDIR, 0770)

def main():
status(“Fetching CIDR list for: %s” % country_code)
cidr_range = country_to_cidr(country_code)
if len(cidr_range) > 0:
fh=open(RESULTSDIR+country_code+’_ranges.txt’, ‘w+’)
for i in cidr_range:
good(“CIDR Ranges for %s saved to: %s\n\n” % (country_code, RESULTSDIR+country_code+’_ranges.txt’))
error(“No CIDR Results Returned!”)
error(“Check country code and try again or follow up manuall…\n\n”)
except KeyboardInterrupt:
error("[WARNING] CTRL+C Detected, Shutting Down…\n\n")

if name == ‘main’:


Hi @Raw-x,

Thanks for the share.

With all my respect, the ORfinder purpose is far from just retrieving CIDR and IP adresses. Moreover, your script doesn’t represent, in any way, an enhancement of the tool mentioned above.

Unrelevant necrobumping, for any articles posted on 0x00sec, is wisely discourage.

Thanks for your comprehension.