ez_pz_hackover_2016
这是在buuctf上找的一道题,还是关于栈溢出的。
checksec查看保护,发现开了FULL RELRO(会随机化elf的地址,got表也变成只读的了),但其他的都关了。
分析源码:
在chall函数里可以发现,如果s的内容和”crashme”相同,便可以进到vuln函数,将s的内容复制到vuln函数的dest里,从而造成溢出。由于字符串比较使用的是strcmp(),所以可以用’\x00’截断”crashme”,然后在后面写入payload。
gdb调试,查看内存空间,发现栈上的空间是可读可写可执行的,所以可以直接将shellcode写进去,然后劫持vuln函数的返回地址到shellcode上来getshell。
接下来要做的就是泄露出dest到ebp的距离,以及计算出写入的shellcode和chall函数里泄露出的那个栈上的地址的偏移量。在vuln函数的function epilogue之前那个nop上下断点,查看一下ebp和刚刚输入的”crashme”的位置
dest到ebp的偏移可以算出来是0x16,而shellcode的起始位置距离ebp是0x8的偏移(0x4盖掉eb ...
pwn1-厦门邀请赛
拿到过后直接分析源码,发现read处存在溢出。
checksec一下,发现除了PIE以外的保护都开了,所以需要想办法泄露canary。
如图所示可以看出,canary被放到了rbp-8的位置(通常都是这里),此时再查看一下main函数的栈,是可以发现read()可以将垃圾数据盖到canary前面,然后利用puts()打印出来。(canary最低位一定是’\x00’,它会打断puts函数的输出,所以这里我们使用sendline(),可以多塞一个’\r’过去,刚好盖掉’\x00’,到时候再补上这个’\x00’就好了。)
如此一来,canary就会被泄露了。但是接下来要思考的是,如何获得shell。分析binary之后发现没有什么好利用的东西,所以去找libc附件上的gadget。
果然,存在execve()去执行shell程序。0x45216这个gadget只需要保证$rax为空就好了,用起来最方便。libc附件是开了PIE保护的,所以需要我们先泄露出它的基址,然后再getshell。剩下的就没什么好说的了。
1234567891011121314151617181920212223 ...
利用__libc_csu_init的通用gadgets
简介:在__libc_csu_init中有以下gadgets,可以帮助我们摆好很多寄存器,或者用来花式rop。
1234567mov rdx, r13mov rsi, r14mov edi, r15dcall qword ptr [r12+rbx*8]
恰好能让rdx和rsi的值被r13和r14控制,而且若能让rbx为0, r12可以调用特定的shellcode,
一般拿来充当ret,找到ROP下一句的位置填进去
而刚好还有如下gadgets
12345678910111213pop rbxpop rbppop r12pop r13pop r14pop r15ret
例题ciscn_s_3
发现vul()是在执行一个read(0, [rsp+buf], 0x400)和write(1, [rsp+buf], 0x30)
读入了0x400个数据显然有缓冲区溢出。
checksec发现只开了NX。
发现gadgets()中有一个
mov rax, 0xf; ret;
和
mov rax, 0x3b re ...
pwn相关命令
objdump -d -M intel (文件名) #查看汇编源码(加intel是想看ASM版本的,默认是AT&T)
readelf -a (文件名) #查看elf文件头信息等
ldd (文件名) #看binary链接到了哪些函数库
nano /root/.gdbinit #同时安装了pwndbg和peda,在这里面注释切换
ps -a #查看所有进程,找到目标进程,在gdb里用at + 进程id调试
#与之配合的,可以在exp相应位置加个空的input()暂停 ...
gdb命令
disas (函数名) #在gdb里反汇编某函数,查看某句指令的地址,好下断点
vm(virtual memory)
at [进程id] #连接到目标进程调试
b (num) #在num行下断点,或者加一个地址,在目标地址下断点
r #重新运行文件
start #单步执行,运行程序,停在第一执行语句
next或n #单步调试(逐过程,不进入函数)
step或s #单步调试(逐语句,会进入函数)
finish #跑完这个函数
c ...
XCTF 3rd-RCTF-2017 Recho
主程序可以看出,就是一直读一个字符串,转换成长度过后用read()读这么多个数据。
长度在小于16的时候会变成16,但是没有上限检查。
checksec发现只开了nx,所以思路应该是打rop。
查看一下字符串
发现了”flag”, 所以可以尝试用open读flag文件,然后read写到内存上,再用printf打印出来。
寻找通用gadget的时候
pop rdi ; ret
pop rdx ; ret
pop rax ; ret
都能找到,但是要控制第二个参数rsi,就只找到了
pop rsi ; pop r15 ; ret
这里就将就着用了。
read和printf是程序自带的,但是open就需要我们自己构造了。
gdb动态调试的时候反汇编一下alarm函数
发现了syscall可以利用。和alarm有0x5的偏移,可以尝试一下got表劫持。
经过一番寻找,发现一个比较方便的gadget,会让rdi存的值所在的地址的值加上rax寄存器低8位的值
0x000000000040070d : add byte ptr [rdi], al ; ret
而这个时候只需要用pop rdi ...
RCTF-2015 welpwn
直接打开ida x64分析源码
发现读了0x400个数据到buf里,刚好没法溢出。于是再查看一下echo函数。
这个for循环特别可疑,会把之前main函数读到的数据赋给s2,直到到有\x00才会停下来。所以这里是一个溢出点。但是我们在覆盖返回地址的时候,肯定会有\x00在里面,所以这里我们最多只能使用一个gadget,剩下的部分就会被截断了。
一开始思路到这里就断了。但是再仔细分析main函数,不难发现buf的起始位置是main函数一开始rsp的位置。好家伙,这下有意思了,s2的起始位置到buf的起始位置起始只隔了0x20!
payload送上去之后,前0x18个位置是不能出现\x00的,接下来0x8可以是一个gadget,再之后就遇上了buf,也就是又会是那0x18个垃圾字符,以及这个gadget。
基于这个特殊的结构,这时就有一种思路了:有没有可能,让前面s2那里的数据和buf的数据能刚好接上,并且只使用一个gadget?答案是有的。用__libc_csu_init上面的pop指令:
0x000000000040089e : pop r13 ...
csaw-ctf-2016-quals warmup
一道盲打题。(fuzz / 模糊测试)
没有给附件,nc上去过后给了一个奇怪的地址0x40060d
盲猜存在栈溢出,尝试一下打进这个地址。但是由于溢出点距离rbp的偏移未知,所以直接写了个脚本爆破。最后发现距离rbp的偏移是0x40,系统是x64的,所以一共填充0x48个垃圾数据。
(由于在try的过程中,结果不一定是我们想要的,所以还是要把回显打印出来看一下,如果不是的话就ctrl+shift+c中断,然后继续爆破)
exp:
1234567891011121314151617181920212223242526from pwn import *#context.log_level = 'debug'backdoor = 0x40060ddef fuzz(p, len, flag): pay = 'a'*len if flag == 0: pay += p32(backdoor) else: pay += p64(backdoor) ...