pwnable_kr_0

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tagOBJ{
struct tagOBJ* fd;
struct tagOBJ* bk;
char buf[8];
}OBJ;
void shell(){
system("/bin/sh");
}
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
int main(int argc, char* argv[]){
malloc(1024);
OBJ* A = (OBJ*)malloc(sizeof(OBJ));
OBJ* B = (OBJ*)malloc(sizeof(OBJ));
OBJ* C = (OBJ*)malloc(sizeof(OBJ));
// double linked list: A <-> B <-> C
A->fd = B;
B->bk = A;
B->fd = C;
C->bk = B;
printf("here is stack address leak: %p\n", &A);
printf("here is heap address leak: %p\n", A);
printf("now that you have leaks, get shell!\n");
// heap overflow!
gets(A->buf);
// exploit this unlink!
unlink(B);
return 0;
}

0x01

这题是比较经典的链表unlink题目,其中unlink的代码如下:

1
2
3
4
5
6
7
8
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}

之前我们学到的malloc中的bin在删除链表节点时有很多限制,比如会比较FD->bk == BK->fd之类的检查,这里的unlink只需要FD->bk,BK->fd指向的地址是可写的。

0x02 leak

题目帮忙leak好了 &A 和 A 的地址~

0x3 思路

我的想法是通过leak的A_addr算好main的ret_addr,然后把void shell的地址存入‘栈’中即可。不过此‘栈’非彼‘栈’,这里需要使用一个技巧,就是我们是无法向栈里面写入shell_addr的,因为unlink时要求写入的地址所指向的内存是可写的,但是shell_addr明显是text段,是readonly的,所以是无法通过unlink将shell_addr写入栈中的;但是转变思路,既然我们无法向栈中写入shellcode,那么我们能不能改变栈的位置呢?比如把ebp指向特定位置,使得栈的位置发生改变,那么相对来说,就是将shellcode写到了栈中去了。

0x4 exploit

查看汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
0x08048555 <+38>: call 0x80483a0 <malloc@plt>
0x0804855a <+43>: add esp,0x10
0x0804855d <+46>: mov DWORD PTR [ebp-0x14],eax //A
0x08048560 <+49>: sub esp,0xc
0x08048563 <+52>: push 0x10
0x08048565 <+54>: call 0x80483a0 <malloc@plt>
0x0804856a <+59>: add esp,0x10
0x0804856d <+62>: mov DWORD PTR [ebp-0xc],eax //B
0x08048570 <+65>: sub esp,0xc
0x08048573 <+68>: push 0x10
0x08048575 <+70>: call 0x80483a0 <malloc@plt>
0x0804857a <+75>: add esp,0x10
0x0804857d <+78>: mov DWORD PTR [ebp-0x10],eax //C

可以发现&A=ebp-0x14,根据leak出来的地址,我们可以得到:ebp = &A+0x14。这样我们就可以构造我们的playload了。

这里有一个关键的东西就是,还是要根据具体程序的代码来选择漏洞利用方式,不同于前面applestore,这里的在leave 之后,程序就要退出了,没有多层的调用,所以ebp不能再次使用,无法改变变量或者栈中其他的值。

但是,这是一个题目嘛,总会有解决办法的。

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
0x80485ff <main+208>: mov ecx,DWORD PTR [ebp-0x4]
=> 0x8048602 <main+211>: leave
0x8048603 <main+212>: lea esp,[ecx-0x4]
0x8048606 <main+215>: ret
________stack________________
gdb-peda$ tele 20
0000| 0xffe69210 --> 0x1
0004| 0xffe69214 --> 0x8643410 --> 0x8643440 --> 0x0
0008| 0xffe69218 --> 0x8643440 --> 0x0
0012| 0xffe6921c --> 0x8643428 --> 0x8643440 --> 0x0
0016| 0xffe69220 --> 0xf77813dc --> 0xf77821e0 --> 0x0
0020| 0xffe69224 --> 0xffe69240 --> 0x1
0024| 0xffe69228 --> 0x0
0028| 0xffe6922c --> 0xf75e6276 (<__libc_start_main+246>: add esp,0x10)
0032| 0xffe69230 --> 0x1
0036| 0xffe69234 --> 0xf7781000 --> 0x1b2db0
0040| 0xffe69238 --> 0x0
0044| 0xffe6923c --> 0xf75e6276 (<__libc_start_main+246>: add esp,0x10)
_______________________
EAX: 0x0
EBX: 0x0
ECX: 0xffe69240 --> 0x1
EDX: 0x8643440 --> 0x0
ESI: 0x1
EDI: 0xf7781000 --> 0x1b2db0
EBP: 0xffe69228 --> 0x0
ESP: 0xffe69210 --> 0x1
EIP: 0x8048602 (<main+211>: leave)

我们查看main函数最后几行汇编代码,结合动态调试,我们发现 ecx = [ebp-0x4],然后后面esp=[ecx-0x4],这样我们控制ebp+0x4这里的内存就可以控制ecx的值,然后控制esp,从而在ret时返回到我们的shellcode上。

playload的构造
chunk

注意:

  1. unlink修改的内存是FD->bk和BK->fd,也就是FD+4和BK+0,这里有一定的偏移。

  2. 注意栈的方向是和正常内存不一样的,所以esp的设置应该大于返回地址。

0x05

exp