首页
社区
课程
招聘
[原创]强网杯S8 Rust Pwn chat-with-me出题思路分享
发表于: 2024-11-4 20:25 3564

[原创]强网杯S8 Rust Pwn chat-with-me出题思路分享

2024-11-4 20:25
3564

本题最终解数为42,因为题目难度不大,总体符合预期。题目是用rust写的代码,同时赛前夜里临时决定删除符号不给源码,一方面导致选手逆向难度很大,另一方面也让大部分选手把精力集中在动调上,避免陷入源码的细节。在出题过程中其实也没有漏洞和明确利用手法的考点,题目是一边学习一边调试的时候出出来的,再次把出题思路分享给大家,算是抛砖引玉。

image

这里还是首先给出源码( rustc 1.82.0-nightly (cefe1dcef 2024-07-22) ):

本题在构思的时候其实是想用不含unsafe的rust语言构造一个漏洞,因此一方面在RustSec上寻找合适的漏洞,另一方面发现了cve-rs项目。首先在RustSec找的漏洞不太适合出题,又限于出题时间和自身水平,最终还是选择用cve-rs内的原理,“盗用”了

UIUCTF 2024 Rusty Pointer题目的触发POC:

据我的理解,这段POC实际上是利用对变量静态生存周期的混淆,欺骗rust编译器不释放离开生存期的变量。

通过上述的技术原理,我们可以得到一个离开生存期仍然可用的指针(或者说对象),在此题中将其用到了栈上对象,因此我们可以获得一个get_ptr​函数内的一个栈对象msg​:

image

而每次add​的时候,由于函数调用的顺序不变,实际上每次申请得到的地址都是一样的。虽说这样让程序逻辑有些奇怪,但是也让堆的可控变小了。另外,Msg​的大小变化其实会导致栈布局,包括该离开生命周期的栈指针的能力发生变化,有时可以直接写到返回地址,这太简单了肯定不行:)

一血战队ACT的解法实际上跟我的预期解是一样的。通过show可以泄露栈上的堆地址、栈地址、ELF地址,通过edit​我们可以发现存在任意地址释放,但是问题在于怎样利用该能力实现栈地址写或者任意地址写。

我们现在手上有两个条件,首先是任意地址释放,其次是rust的vec​类似于C++,使用realloc​扩容,其指针数组也存储在堆上。

image

因此不难想到通过释放伪造堆块,将vec​的指针数组劫持到我们可控的位置,而我们可控的位置最直接的就是栈上0x50的空间,另一个是stdin的输入在堆上的缓冲区

image

实际上从选手做法来看,这两个位置都可以成功伪造堆块,实现控制vec​的指针。

这里给出我的exp:

实际上在输入choice​的时候输入的字符会开辟新的堆空间存储,因此也有队伍把栈上伪造的堆释放到tcache,接着实现栈溢出。

image

此外还有选手发现delete​的时候实际上会申请堆块,这我就没有往下深入研究了。

use std::fmt;
use std::io::{self, Read, Write};
 
const MAX_MSG_LEN: usize = 0x50;
struct Msg {
    data: [u8; MAX_MSG_LEN],
}
 
impl Msg {
    #[inline(never)]
    fn new() -> Self {
        Msg {
            data: [0; MAX_MSG_LEN],
        }
    }
 
}
 
impl fmt::Display for Msg {
    #[inline(never)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.data)
    }
}
 
#[inline(never)]
fn prompt(msg: String) {
    print!("{} > ", msg);
    io::stdout().flush().unwrap();
}
 
struct ChatBox {
    msg_list: Vec<&'static mut Msg>,
 
}
 
impl ChatBox {
    #[inline(never)]
    fn new() -> Self {
        ChatBox {
            msg_list: Vec::new(),
        }
    }
 
    #[inline(never)]
    fn add_msg(&mut self) {
        println!("Adding a new message");
        self.msg_list.push(self.get_ptr());
        println!(
            "Successfully added a new message with index: {}",
            self.msg_list.len() - 1
        );
    }
 
    #[inline(never)]
    fn show_msg(&mut self) {
        prompt("Index".parse().unwrap());
        let mut index = String::new();
        io::stdin().read_line(&mut index).expect("Failed to read");
        let index: usize = index.trim().parse().expect("Invalid!");
        println!("Content: {}", self.msg_list[index]);
    }
 
    #[inline(never)]
    fn edit_msg(&mut self) {
        prompt("Index".parse().unwrap());
        let mut index = String::new();
        io::stdin().read_line(&mut index).expect("Failed to read");
        let index: usize = index.trim().parse().expect("Invalid!");
        prompt("Content".parse().unwrap());
        let mut handle = io::stdin().lock();
        handle.read(&mut self.msg_list[index].data).expect("Failed to read");
        println!("Content: {}", self.msg_list[index]);
    }
 
    #[inline(never)]
    fn delete_msg(&mut self) {
        prompt("Index".parse().unwrap());
        let mut index = String::new();
        io::stdin().read_line(&mut index).expect("Failed to read");
        let index: usize = index.trim().parse().expect("Invalid!");
        self.msg_list.remove(index);
    }
 
    #[inline(never)]
    fn get_ptr(&self) -> &'static mut Msg {
        const S: &&() = &&();
 
        fn get_ptr<'a, 'b, T: ?Sized>(x: &'a mut T) -> &'b mut T {
            fn ident<'a, 'b, T: ?Sized>(_val_a: &'a &'b (), val_b: &'b mut T) -> &'a mut T {
                val_b
            }
            let f: fn(_, &'a mut T) -> &'b mut T = ident;
            f(S, x)
        }
        let mut msg = Msg::new();
        get_ptr(&mut msg)
    }
}
 
 
#[inline(never)]
fn main() {
    let mut chat_box = ChatBox::new();
    println!("I am a chatting bot of QWB S8, you can chat with me.");
    println!("If you delight me, I will give you flag!");
    println!("This is function menu: ");
    println!("1. add");
    println!("2. show");
    println!("3. edit");
    println!("4. delete");
    println!("5. exit");
    loop {
        prompt("Choice".parse().unwrap());
        let mut choice = String::new();
        io::stdin().read_line(&mut choice).expect("Failed to read");
        let choice: i8 = choice.trim().parse().expect("Invalid!");
 
        match choice {
            1 => chat_box.add_msg(),
            2 => chat_box.show_msg(),
            3 => chat_box.edit_msg(),
            4 => chat_box.delete_msg(),
            5 => break,
            _ => println!("Invalid Choice!")
        }
    }
}
use std::fmt;
use std::io::{self, Read, Write};
 
const MAX_MSG_LEN: usize = 0x50;
struct Msg {
    data: [u8; MAX_MSG_LEN],
}
 
impl Msg {
    #[inline(never)]
    fn new() -> Self {
        Msg {
            data: [0; MAX_MSG_LEN],
        }
    }
 
}
 
impl fmt::Display for Msg {
    #[inline(never)]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.data)
    }
}
 
#[inline(never)]
fn prompt(msg: String) {
    print!("{} > ", msg);
    io::stdout().flush().unwrap();
}
 
struct ChatBox {
    msg_list: Vec<&'static mut Msg>,
 
}
 
impl ChatBox {
    #[inline(never)]
    fn new() -> Self {
        ChatBox {
            msg_list: Vec::new(),
        }
    }
 
    #[inline(never)]
    fn add_msg(&mut self) {
        println!("Adding a new message");
        self.msg_list.push(self.get_ptr());
        println!(
            "Successfully added a new message with index: {}",
            self.msg_list.len() - 1
        );
    }
 
    #[inline(never)]
    fn show_msg(&mut self) {
        prompt("Index".parse().unwrap());
        let mut index = String::new();
        io::stdin().read_line(&mut index).expect("Failed to read");
        let index: usize = index.trim().parse().expect("Invalid!");
        println!("Content: {}", self.msg_list[index]);
    }
 
    #[inline(never)]
    fn edit_msg(&mut self) {
        prompt("Index".parse().unwrap());
        let mut index = String::new();
        io::stdin().read_line(&mut index).expect("Failed to read");
        let index: usize = index.trim().parse().expect("Invalid!");
        prompt("Content".parse().unwrap());
        let mut handle = io::stdin().lock();
        handle.read(&mut self.msg_list[index].data).expect("Failed to read");
        println!("Content: {}", self.msg_list[index]);
    }
 
    #[inline(never)]
    fn delete_msg(&mut self) {
        prompt("Index".parse().unwrap());
        let mut index = String::new();
        io::stdin().read_line(&mut index).expect("Failed to read");
        let index: usize = index.trim().parse().expect("Invalid!");
        self.msg_list.remove(index);
    }
 
    #[inline(never)]
    fn get_ptr(&self) -> &'static mut Msg {
        const S: &&() = &&();
 
        fn get_ptr<'a, 'b, T: ?Sized>(x: &'a mut T) -> &'b mut T {
            fn ident<'a, 'b, T: ?Sized>(_val_a: &'a &'b (), val_b: &'b mut T) -> &'a mut T {
                val_b
            }
            let f: fn(_, &'a mut T) -> &'b mut T = ident;
            f(S, x)
        }
        let mut msg = Msg::new();
        get_ptr(&mut msg)
    }
}
 
 
#[inline(never)]
fn main() {
    let mut chat_box = ChatBox::new();
    println!("I am a chatting bot of QWB S8, you can chat with me.");
    println!("If you delight me, I will give you flag!");
    println!("This is function menu: ");
    println!("1. add");
    println!("2. show");
    println!("3. edit");
    println!("4. delete");
    println!("5. exit");
    loop {
        prompt("Choice".parse().unwrap());
        let mut choice = String::new();
        io::stdin().read_line(&mut choice).expect("Failed to read");
        let choice: i8 = choice.trim().parse().expect("Invalid!");
 
        match choice {
            1 => chat_box.add_msg(),
            2 => chat_box.show_msg(),
            3 => chat_box.edit_msg(),
            4 => chat_box.delete_msg(),
            5 => break,
            _ => println!("Invalid Choice!")
        }
    }
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 2
支持
分享
最新回复 (6)
雪    币: 25
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
狠狠学习了
2024-11-4 20:26
1
雪    币: 1035
活跃值: (475)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
3
狠狠学习了
2024-11-4 22:29
1
雪    币: 10850
活跃值: (5976)
能力值: ( LV15,RANK:533 )
在线值:
发帖
回帖
粉丝
4
狠狠学习了
2024-11-5 14:07
0
雪    币: 380
活跃值: (997)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
狠狠学习了
2024-11-5 16:01
1
雪    币: 1404
活跃值: (677)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
狠狠学习了
2024-11-7 17:12
0
雪    币: 32
活跃值: (500)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
狠狠学习了
2024-11-27 16:23
0
游客
登录 | 注册 方可回帖
返回
//