脚本中gdb调试

之前不知道自己哪里有问题,现在懂怎么改了

from pwn import *
context(log_level='debug', arch='amd64', os='linux')
context.terminal = ["tmux", "splitw", "-h"]
p=process('./pwn')
gdb.attach(p,'b* 0x4013C2')

第三行是关键点,之后在想gdb调试的地方添加gdb.attach语句即可

使用时,先提升终端的权限为root,再输入tmux,之后进入tmux的界面之后运行脚本即可

简化书写

uu64 = lambda x: u64(x.ljust(8, b'\x00'))
s = lambda x: p.send(x)
sa = lambda x, y: p.sendafter(x, y)
sl = lambda x: p.sendline(x)
sla = lambda x, y: p.sendlineafter(x, y)
ru = lambda x: p.recvuntil(x)
ti = lambda : p.interactive()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))

沙盒检测

seccomp-tools dump ./pwn

orw

shellcode=''
shellcode+=shellcraft.open('flag',0,0)
shellcode+=shellcraft.read('rax',addr_mpro+0x800,0x100)
shellcode+=shellcraft.write(1,addr_mpro+0x800,'rax')# 1 stdout 2 stderr
shellcode=asm(shellcode)

可以发现,程序禁用了execve和execveat,不能直接get shell,需要通过orw

同时,程序也禁用了常规的open read write,需要我们找到他们的替代品

对于open,我们可以选择使用openat或者openat2(本题已禁用)

对于read,我们可以选择使用readv,preadv,preadv2(本题可用),pread64或者mmap(本题可用)

对于write,我们可以选择使用writev(本题可用),sendfile(本题可用,且能省略read)等

https://blog.csdn.net/qq_54218833/article/details/134205383

shellcode的时候利用这个,rop的时候不要用libc里面的open函数,而是pop rax 然后syscall(目前还不知道原理,猜测是libc内部的open函数其实不是sys_open,因为那个题只让用open read write,其他系统调用都禁用了)

mprotect

如果能够rop的话,可以尝试使用mprotect

mprotect之后,read,然后写入shellcode,然后返回shellcode地址

mprotect 函数用于设置一块内存的保护权限(将从 start 开始、长度为 len 的内存的保护属性修改为 prot 指定的值),函数原型如下所示:

#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);

需要注意的是,指定的内存区间必须包含整个内存页(4K),起始地址 start 必须是一个内存页的起始地址,并且区间长度 len 必须是页大小的整数倍。

start必须与0x1000对齐。

当一个进程的内存访问行为违背了内存的保护属性,内核将发出 SIGSEGV(Segmentation fault,段错误)信号,并且终止该进程。

https://firmianay.gitbook.io/ctf-all-in-one/4_tips/4.11_mprotect

ROPgadget

ROPgadget --binary './libc.so.6' --multibr |grep 'syscall'|grep 'ret'

注意–multibr这个参数,不然出不来syscall;ret这个

https://github.com/JonathanSalwan/ROPgadget/issues/145

xchg edx, eax ;

交换edx eax的值,如果找不到pop edx 可以考虑这个片段

scanf(“%d”)

正常输入时,输入为范围在-2147483648~2147483647内的整数。

如果输入范围在-9223372036854775808~9223372036854775807内的整数,则会截断高位读取,此范围是long long int的范围。

如果输入范围在long long int范围之外,则统一将参数赋值为-1(0xFFFFFFFF)

如果输入为非数字,分为下列情况:

– (1) 如果输入仅有一个,则该输入无效,该值不变。

– (2) 如果输入有数字前缀(如12345abcd),则scanf仅会读取前面的数字,从第一个非数字开始,后面全部舍弃(12345)。

– (3) 如果输入有多个且使用一个scanf语句(如scanf(“%d, %d”, &a, &b))。输入第一个非数字后,后面的所有输入均为无效,前面的输入可以赋值。

– (4) 如果输入有多个且使用多个scanf语句(含循环,即一个scanf中仅有一个输入),则输入非数字时,如果输入的不是’+’或’-’,则后面紧跟的所有scanf均自动跳过,变为无效,不能输入。如果输入的是’+’或’-’,则会跳过当前输入,后面仍然可以进行输入。

摘抄自:https://blog.csdn.net/qq_54218833/article/details/121308367

改写got表

确认保护只有RELRO: Partial RELRO

如果是Full RELRO则GOT不可写(mprotect尝试一下?)

另外got表被程序填入正确地址前后都可以改写,填入之前可以往里面填入别的函数的plt地址,改写之后可以填入别的函数的真实地址

ret2csu

https://www.yuque.com/hxfqg9/bin/pqc1nq

写文章的时候已经忘了,后面看还能补上不。

栈迁移

https://www.cnblogs.com/max1z/p/15299000.html

当栈溢出只能最多溢出到返回地址,则可以考虑栈迁移,基础的栈迁移看上面这个链接

一般来说题目不会基础

https://bbs.kanxue.com/thread-258030-1.htm

稍复杂的就是第一次ret到read的地址(main函数里输入的地址),然后在第二次read的时候读入rop,然后ret到leave_ret。

pwndbg调试忽略信号

pwndbg> handle SIGALRM nostop 
Signal Stop Print Pass to program Description
SIGALRM No Yes No Alarm clock

handle SIGALRM nostop

nostop:让调试器在接收到 SIGALRM 信号时不要停止程序。

noprint:不显示任何关于接收到信号的消息。

pass:允许信号传递给被调试的程序。