LCTF2017—…
这次CTF做的可以说很憋屈了。唉,辛辛苦苦两天,愣是没做出来一道题,最后全靠学弟做出来一道逆向才避免了光头的尴尬。
、
Web弱鸡也就做做Web、MISC,以后再学学可能做做Crypto。
太菜了。
MISC-拿去当壁纸吧朋友
题目给了一篇论文
《Keyless dynamic optimal multi-bit image steganography using energetic pixels》Goutam Paul著
Google了一下,发现有一个Github项目叫BusySteg。
down下来之后,看了一下,要安opencv,然后cmake编译运行即可。
然后我就死在了安opencv的过程中。
看过Write up之后,发现。。。我果然好菜。。QAQ
Web-签到题
我没做出来签到题,所以这次比赛算没打的。。。。。
QAQ
题目是一道test.php
稍微扫了一下,发现网站参数就这么一个。www.baidu.com
然后一想,肯定是用协议做文章,很自然的想到file协议,而后。。。我就陷入了试127.0.0.1 、localhost的误区。
也曾想到网站后加子目录,但始终没想明白为何wp里payload会是file://www.baidu.com/etc/flag?
尤其是那个etc。
现在算是明白了,问了出题人,说随便放的,因为都会去试etc/passwd文件。。。。。然后就有线索了,这个文件的最后会有路径。。。我怎么就没想着试试。。。。。
Web-Simple blog
扫一下目录知道了存在login.php、admin.php两个文件,访问admin.php发现有权限控制,login.php是登录界面。
还扫到了弱口令,账号密码admin,可以进admin.php
这里,wp里说,可以通过login.php和admin.php的备份文件即.php.swp获得备份文件。
以此获得源码。
显然,我没找到。。。。
login.php源码:
function get_identity(){
global $id;
$token = get_random_token();
$c = openssl_encrypt($id, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token);
$_SESSION['id'] = base64_encode($c);
setcookie("token", base64_encode($token));
if($id==='admin'){
$_SESSION['isadmin'] = 1;
}else{
$_SESSION['isadmin'] = 0;
}
}
function test_identity(){
if (isset($_SESSION['id'])) {
$c = base64_decode($_SESSION['id']);
$token = base64_decode($_COOKIE["token"]);
if($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token)){
if ($u === 'admin') {
$_SESSION['isadmin'] = 1;
return 1;
}
}else{
die("Error!");
}
}
return 0;
}
可以看到在session中也做了身份验证,但是由于加密模式是aes-128-cbc
,且$token
在cookie里,可控,所以这里可以进行Pading Oracle Attack,通过修改$token
可以把$_SESSION['isadmin']
改为1,如此,即可登入admin.php。
学习了,Pading Oracle Attack。出题大佬的blog此文:http://f1sh.site/2017/08/04/%E5%88%9D%E5%AD%A6padding-oracle-attack/
admin.php源码:
if(isset($_GET['id'])){
$id = mysql_real_escape_string($_GET['id']);
if(isset($_GET['title'])){
$title = mysql_real_escape_string($_GET['title']);
$title = sprintf("AND title='%s'", $title);
}else{
$title = '';
}
$sql = sprintf("SELECT * FROM article WHERE id='%s' $title", $id);
$result = mysql_query($sql,$con);
$row = mysql_fetch_array($result);
if(isset($row['title'])&&isset($row['content'])){
echo "<h1>".$row['title']."</h1><br>".$row['content'];
die();
}else{
die("This article does not exist.");
}
wp说此处可以利用PHP格式化字符串的漏洞。在PHP的sprintf这个函数中%\会被当成一个格式化字符串。
%\会被认为是个tan 90'的格式化字符串,输出空,so,可以传入title=%' or 1#
,转义,拼接
sprintf("SELECT * FROM article WHERE id='%s' AND title='%\' or 1#'", $id);
这样%就会吃掉后面的\组成一个格式化字符串,单引号就成功逃逸了出来。
娘希匹,想不到,真的想不到。。。。
只是这样的话还是会报错参数不足,因为这条代码里有两个格式化字符串但是只有一个参数。不过PHP的格式化字符串还有另一种表示方法%1$s,其中%后面的数字就表示引用第几个参数,$后面是格式化字符串的类型。
传入title=%1$' or 1#
时:
sprintf("SELECT * FROM article WHERE id='%s' AND title='%1$\' or 1#'", $id);
最终payload:
?id=0&title=%251%24'%20union%20select%201%2C2%2C3%23
注入时也有一个小坑,key这个表名是MYSQL保留字,我们把它当做表名带入查询时必须用反引号包起来。