攻防世界高手进阶区 ——forgot
攻防世界高手进阶区 ——forgot
看了半天,啥也没看懂,做出来了才发现啥也不是。
一,分析文件
checksec
还好,只开启了堆栈不可执行。
运行一下
翻译了一下,应该是判断邮箱是否合乎规矩。
直接ida
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
81int __cdecl main()
{
size_t v0; // ebx
char v2[32]; // [esp+10h] [ebp-74h] BYREF
_DWORD v3[10]; // [esp+30h] [ebp-54h]
char s[32]; // [esp+58h] [ebp-2Ch] BYREF
int v5; // [esp+78h] [ebp-Ch]
size_t i; // [esp+7Ch] [ebp-8h]
v5 = 1;
v3[0] = sub_8048604; // 函数指针数组
v3[1] = sub_8048618;
v3[2] = sub_804862C;
v3[3] = sub_8048640;
v3[4] = sub_8048654;
v3[5] = sub_8048668;
v3[6] = sub_804867C;
v3[7] = sub_8048690;
v3[8] = sub_80486A4;
v3[9] = sub_80486B8;
puts("What is your name?");
printf("> ");
fflush(stdout); // 清空输出缓冲区
fgets(s, 32, stdin); // 从输入流获取32个字节
sub_80485DD(s);
fflush(stdout);
printf("I should give you a pointer perhaps. Here: %x\n\n", sub_8048654);
fflush(stdout);
puts("Enter the string to be validate");
printf("> ");
fflush(stdout);
scanf("%s", v2);
for ( i = 0; ; ++i )
{
v0 = i;
if ( v0 >= strlen(v2) )
break;
switch ( v5 )
{
case 1:
if ( sub_8048702(v2[i]) ) // 判断是否为小写字母,数字,dot,+-,下划线
v5 = 2;
break;
case 2:
if ( v2[i] == 64 ) // @
v5 = 3;
break;
case 3:
if ( sub_804874C(v2[i]) ) // 小写字母,数字,下划线
v5 = 4;
break;
case 4:
if ( v2[i] == 46 ) // dot
v5 = 5;
break;
case 5:
if ( sub_8048784(v2[i]) ) // 小写字母
v5 = 6;
break;
case 6:
if ( sub_8048784(v2[i]) ) // 小写字母
v5 = 7;
break;
case 7:
if ( sub_8048784(v2[i]) ) // 小写字母
v5 = 8;
break;
case 8:
if ( sub_8048784(v2[i]) ) // 小写字母
v5 = 9;
break;
case 9:
v5 = 10;
break;
default:
continue;
}
}
((void (*)(void))v3[--v5])(); // 将v3转换为函数指针,并转到v3
return fflush(stdout);
}刚开始死活看不懂,后来发现是自己的C语言基础太薄弱了,还有就是不会汇编。(菜鸡的我表示该学学深入学学C语言和汇编了)。
这里关键的一步是
1
((void (*)(void))v3[--v5])(); // 将v3转换为函数指针,并转到v3
强制转换v3为函数指针并转到。
不会函数指针的我后面有解释。
二,解题思路
通过f12+shift查找string
跳转后找到了该函数
1
2
3
4
5
6
7int sub_80486CC()
{
char s[58]; // [esp+1Eh] [ebp-3Ah] BYREF
snprintf(s, 0x32u, "cat %s", "./flag");
return system(s);
}就是打开flag文件的函数。
仔细观察后,发现scanf这里存在栈溢出漏洞
1
scanf("%s", v2)
查看main函数的栈后,发现只需要溢出0x20个字节就可以覆盖v3的值
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-00000074 var_74 db 32 dup(?)
-00000054 db ? ; undefined//这里是v3的地址
-00000053 db ? ; undefined
-00000052 db ? ; undefined
-00000051 db ? ; undefined
-00000050 db ? ; undefined
-0000004F db ? ; undefined
-0000004E db ? ; undefined
-0000004D db ? ; undefined
-0000004C db ? ; undefined
-0000004B db ? ; undefined
-0000004A db ? ; undefined
-00000049 db ? ; undefined
-00000048 db ? ; undefined
-00000047 db ? ; undefined
-00000046 db ? ; undefined
-00000045 db ? ; undefined
-00000044 db ? ; undefined
-00000043 db ? ; undefined
-00000042 db ? ; undefined
-00000041 db ? ; undefined
-00000040 db ? ; undefined
-0000003F db ? ; undefined
-0000003E db ? ; undefined
-0000003D db ? ; undefined
-0000003C db ? ; undefined
-0000003B db ? ; undefined
-0000003A db ? ; undefined
-00000039 db ? ; undefined
-00000038 db ? ; undefined
-00000037 db ? ; undefined
-00000036 db ? ; undefined
-00000035 db ? ; undefined
-00000034 db ? ; undefined
-00000033 db ? ; undefined
-00000032 db ? ; undefined
-00000031 db ? ; undefined
-00000030 db ? ; undefined
-0000002F db ? ; undefined
-0000002E db ? ; undefined
-0000002D db ? ; undefined
-0000002C s db ?
-0000002B db ? ; undefined
-0000002A db ? ; undefined
-00000029 db ? ; undefined
-00000028 db ? ; undefined
-00000027 db ? ; undefined
-00000026 db ? ; undefined
-00000025 db ? ; undefined
-00000024 db ? ; undefined
-00000023 db ? ; undefined
-00000022 db ? ; undefined
-00000021 db ? ; undefined
-00000020 db ? ; undefined
-0000001F db ? ; undefined
-0000001E db ? ; undefined
-0000001D db ? ; undefined
-0000001C db ? ; undefined
-0000001B db ? ; undefined
-0000001A db ? ; undefined
-00000019 db ? ; undefined
-00000018 db ? ; undefined
-00000017 db ? ; undefined
-00000016 db ? ; undefined
-00000015 db ? ; undefined
-00000014 db ? ; undefined
-00000013 db ? ; undefined
-00000012 db ? ; undefined
-00000011 db ? ; undefined
-00000010 db ? ; undefined
-0000000F db ? ; undefined
-0000000E db ? ; undefined
-0000000D db ? ; undefined
-0000000C anonymous_0 dd ?
-00000008 anonymous_1 dd ?
-00000004 var_4 dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008
+00000008 ; end of stack variables1
_DWORD v3[10]; // [esp+30h] [ebp-54h]
然后可以通过后面的强制转换并执行来访问该v3函数,便只需要覆盖v3的地址为getflag的地址即可
exp
- 这里只能覆盖大写的A,否则会更改v5的值,就要覆盖其他的地址了。
1 | from pwn import * |
- 如果非要用小写的话,下面这个exp也可以。(小写a将v5改为了2)
1 | from pwn import * |
因为
1 | _DWORD v3[10]; // [esp+30h] [ebp-54h] |
v3为_DWORD,DWORD 代表 unsigned long
在32位编译器中unsigned long有4个字节,所以覆盖量为0x20 +4 = 36。
函数指针数组
一、什么是函数指针
在定义函数指针数组之前,需要首先知道什么是函数指针。函数指针的定义形象点来说,就是用一个指针变量代替原函数中的函数名位置。
原函数
int Add(int a,int b)
函数指针定义(不需要加入&取值符号,因为函数标识实际上就是一个地址)
int (*p)(int ,int)
p=Add
调用(直接替代原有标识,类比数组传参)
p(A,B)
二、函数指针数组的定义
情况假定:编写一个这样一个数组,这一个数组可以用于索引一系列的排序函数
实现其实非常简单我们只需在改写(*p)为(*p[MAX])就行了
实现:
1 | int(*sort[]) = |
引用自怎样定义函数指针数组 - whitesad - 博客园 (cnblogs.com)
C表达式((void (*)(void))0();
通过一步步来讲解:
没有参数和不返回值
1 | void f(void)1 |
定义一个指针没有参数和返回值
1 | void (*p)(void)1 |
定义一个仅有类型的指针
1 | (void(*))(void)1 |
定义一个强转类型(类型定义在括号内,跟着一个值)
1 | (void (*)(void))01 |
到目前为止我们定义了一个由0强转成一个指向函数且返回值。
这个转换时一个指针到函数的类型。
1 | (your expression here)(arguments to the function)1 |
以0位值,转换成指针指向函数(没有参数,没有返回值),之后在调用。
1 | ((void (*)(void))0)(/*no args*/); |