VNCTF2023 Web

象棋王子
f12找到play.js,翻到最后的输出flag的逻辑,如果my==1,那么就会执行一段jsfuck语句,直接把这个复制粘贴到控制台运行即可。

电子木鱼
use actix_files::Files;
use actix_web::{
error, get, post,
web::{self, Json},
App, Error, HttpResponse, HttpServer,
};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
use tera::{Context, Tera};
static GONGDE: Lazy<ThreadLocker<i32>> = Lazy::new(|| ThreadLocker::from(0));
#[derive(Debug, Clone, Default)]
struct ThreadLocker<T> {
value: Arc<Mutex<T>>,
}
impl<T: Clone> ThreadLocker<T> {
fn get(&self) -> T {
let mutex = self.value.lock().unwrap();
mutex.clone()
}
fn set(&self, val: T) {
let mut mutex = self.value.lock().unwrap();
*mutex = val;
}
fn from(val: T) -> ThreadLocker<T> {
ThreadLocker::<T> {
value: Arc::new(Mutex::new(val)),
}
}
}
#[derive(Serialize)]
struct APIResult {
success: bool,
message: &'static str,
}
#[derive(Deserialize)]
struct Info {
name: String,
quantity: i32,
}
#[derive(Debug, Copy, Clone, Serialize)]
struct Payload {
name: &'static str,
cost: i32,
}
const PAYLOADS: &[Payload] = &[
Payload {
name: "Cost",
cost: 10,
},
Payload {
name: "Loan",
cost: -1_000,
},
Payload {
name: "CCCCCost",
cost: 500,
},
Payload {
name: "Donate",
cost: 1,
},
Payload {
name: "Sleep",
cost: 0,
},
];
#[get("/")]
async fn index(tera: web::Data<Tera>) -> Result<HttpResponse, Error> {
let mut context = Context::new();
context.insert("gongde", &GONGDE.get());
if GONGDE.get() > 1_000_000_000 {
context.insert(
"flag",
&std::env::var("FLAG").unwrap_or_else(|_| "flag{test_flag}".to_string()),
);
}
match tera.render("index.html", &context) {
Ok(body) => Ok(HttpResponse::Ok().body(body)),
Err(err) => Err(error::ErrorInternalServerError(err)),
}
}
#[get("/reset")]
async fn reset() -> Json<APIResult> {
GONGDE.set(0);
web::Json(APIResult {
success: true,
message: "重开成功,继续挑战佛祖吧",
})
}
#[post("/upgrade")]
async fn upgrade(body: web::Form<Info>) -> Json<APIResult> {
if GONGDE.get() < 0 {
return web::Json(APIResult {
success: false,
message: "功德都搞成负数了,佛祖对你很失望",
});
}
if body.quantity <= 0 {
return web::Json(APIResult {
success: false,
message: "佛祖面前都敢作弊,真不怕遭报应啊",
});
}
if let Some(payload) = PAYLOADS.iter().find(|u| u.name == body.name) {
let mut cost = payload.cost;
if payload.name == "Donate" || payload.name == "Cost" {
cost *= body.quantity;
}
if GONGDE.get() < cost as i32 {
return web::Json(APIResult {
success: false,
message: "功德不足",
});
}
if cost != 0 {
GONGDE.set(GONGDE.get() - cost as i32);
}
if payload.name == "Cost" {
return web::Json(APIResult {
success: true,
message: "小扣一手功德",
});
} else if payload.name == "CCCCCost" {
return web::Json(APIResult {
success: true,
message: "功德都快扣没了,怎么睡得着的",
});
} else if payload.name == "Loan" {
return web::Json(APIResult {
success: true,
message: "我向佛祖许愿,佛祖借我功德,快说谢谢佛祖",
});
} else if payload.name == "Donate" {
return web::Json(APIResult {
success: true,
message: "好人有好报",
});
} else if payload.name == "Sleep" {
return web::Json(APIResult {
success: true,
message: "这是什么?床,睡一下",
});
}
}
web::Json(APIResult {
success: false,
message: "禁止开摆",
})
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let port = std::env::var("PORT")
.unwrap_or_else(|_| "2333".to_string())
.parse()
.expect("Invalid PORT");
println!("Listening on 0.0.0.0:{}", port);
HttpServer::new(move || {
let tera = match Tera::new("src/templates/**/*.html") {
Ok(t) => t,
Err(e) => {
println!("Error: {}", e);
::std::process::exit(1);
}
};
App::new()
.app_data(web::Data::new(tera))
.service(Files::new("/asset", "src/templates/asset/").prefer_utf8(true))
.service(index)
.service(upgrade)
.service(reset)
})
.bind(("0.0.0.0", port))?
.run()
.await
}
当name=Cost
时,cost为10
,而cost是i32类型的,这里就有可能发生整数溢出

Rust的整数溢出补码机制:
补码是一种用于表示有符号整数的方法,其中最高位被用于表示符号,而其他位用于表示数值的大小。在i32类型中,最高位(第32位)表示符号,0表示正数,1表示负数
当i32类型发生正数溢出时,即整数值大于2^31-1时,最高位将被设置为1,表示负数,而其他位表示溢出后的值。例如,当i32类型的值从2147483647(0x7fffffff)加1时,结果将变为-2147483648(0x80000000)
当i32类型发生负数溢出时,即整数值小于-2^31时,最高位将被设置为0,表示正数,而其他位表示溢出后的值。例如,当i32类型的值从-2147483648(0x80000000)减1时,结果将变为2147483647(0x7fffffff)
那么我们构造一个溢出的cost,再补码之后小于-1000000001
即可
例:当quantity为2000000000时,cost为20000000000。此时超过了i32的范围,那么需要减去i32的范围,直到满足i32即可。20000000000-2^31*2*5=-1474836480

然后这里反转一下,就变成了1474836480>1000000001

