2025愚人节专场 | Writeup

游戏 1 根据题目名字和提示的1.8,猜测是游戏minecraft,应该是开放了服务器端口 先下载游戏https://github.com/HMCL-dev/HMCL/releases/tag/release-3.6.12 选择你对应的版本即可 因为我是macos,所以选择.jar版 确保系统安装了openjdk后,直接启动 第一次启动没有游戏版本,可以点击右边的版本列表下载1.8的版本。然后随便创建一个离线账户名,启动游戏 进去后选择多人游戏,添加服务器,服务器地址填容器给的地址 看到绿色信号图标说明连接成功,点击即可进入 进来后在附近走一走,看到这个牌子 猜测应该有个用户叫admin,按esc断开链接,退出游戏,将刚才添加的用户名删除,添加一个admin 然后再此链接,会发现没有了血条,直接变成了创造模式,说明应该是进入成功 接下来有两种解法 解法1 可以看到上图前面有一个石块在这里显得很突兀,我们可以用/gamemode 3开启透视模式看一下下面藏了什么。可以发现正下方有个小房间,飞下去看一下有什么 看到有一个箱子,用/gamemode 1切换回正常模式后就能打开箱子就能看到有一本password。在最后一页就能看到一串账号密码 解法2 一样进入到admin,之后直接用/kill自杀复活重生点即可直接跳转到带密码箱的小房间 游戏 2 开启容器后尝试访问发现并不能访问。并且结合上一题拿到的admin和密码,猜测是ssh,直接登陆 成功,探索了一圈后发现sudo藏了东西 可以执行/opt/get_flag,看一下这是什么 可以发现是一串shell脚本。并且直接读取flag,但读出来的内容会被tr干掉,所以就要想办法怎么绕这个通配符 先来做个小实验 正常来说会直接过滤掉,但如果我这样做呢? 会发现除了a以外的字符都跳了出来,并且我的a文件是空的。这就说明:在[]进行匹配时会将当前目录下符合匹配条件的文件优先展开,并以丢弃原本的所有匹配符,只以文件名作为通配符内容。也就是说在上面这个例子里,通配符的内容其实就变成了只有[a] 那么答案就显而易见,我们随便找个大写字母创建文件就行,因为flag一般不会有大写符号(后面也给了提示)。并且根据脚本得知工作路径在tmp,所以就在tmp创建

April 1, 2025 · 1 min · Red

24-25下半学年第二次线上赛 | Writeup

莫斯档案馆 每一层有一个pwd.png和一个压缩包,pwd.png是一个彩色条纹和圆点的非常小的图像,也就是摩斯密码,套了很多很多层,写脚本去识别摩斯密码,并递归解压。 from PIL import Image import re def getMorse(image): """ 从图像中提取莫尔斯电码 假定背景颜色是固定的,莫尔斯电码具有不同的颜色。 莫尔斯电码可以是任何颜色,只要它与左上像素的颜色不同。 >>> getMorse('pwd.png') ['----.'] """ im = Image.open(image, 'r') chars = [] background = im.getdata()[0] for i, v in enumerate(list(im.getdata())): if v == background: chars.append(" ") else: chars.append("*") output = "".join(chars) # 清理输出,去除前后的空白 # 然后将每组3个星号转换为短横线 # 将星号转换为实际的点 # 将字母之间的空格(即>1个背景像素)转换为分隔符 # 删除空白 # 返回字母的列表 output = re.sub(r'^\s*', '', output) output = re.sub(r'\s*$', '', output) output = re.sub(r'\*{3}', '-', output) output = re.sub(r'\*', '.', output) output = re.sub(r'\s{2,}', ' | ', output) output = re.sub(r'\s', '', output) output = output.split('|') return output def getPassword(morse): """ 解码莫尔斯电码 将莫尔斯电码转换回文本。 以字母列表为输入,返回转换后的文本。 注意,挑战使用小写字母。 >>> getPassword(['----.']) '9' """ MORSE_CODE_DICT = { '.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E', '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X', '-.--': 'Y', '--..': 'Z', '-----': '0', '.----': '1', '..---': '2', '...--': '3', '....-': '4', '.....': '5', '-....': '6', '--...': '7', '---..': '8', '----.': '9', '-..-.': '/', '.-.-.-': '.', '-.--.-': ')', '..--..': '?', '-.--.': '(', '-....-': '-', '--..--': ',' } for item in morse: return "".join([MORSE_CODE_DICT.get(item) for item in morse]).lower() def main(): """ 自动启动 用于自动化。 自动调用方法并使用'pwd.png'作为输入图像。 """ print(getPassword(getMorse("pwd.png"))) if __name__ == "__main__": main() 并使用sh脚本去循环执行 ...

March 31, 2025 · 9 min · Red

2025长城杯半决赛 | Writeup

AWDP CCforum 粗略审计后大概可以知道应该是要先登陆进admin.php,但sql语句全部做了预编译所以没法注入,试了一下通过爆破可以获取到admin.php的账号密码。登陆进去后再看有什么利用点 入口没有什么特别的,可以直接先看到login的逻辑 可以看到把通过验证的用户名写到了session里,接着看reply.php和post.php会发现都有一个敏感词过滤。并且他们调用的$username都是session里的明文用户名 跟进到config.php看这两个函数 可以看到record_banned函数里主要干了三件事 拿用户名base64的结果创建目录 判断是否创建成功,如果创建成功就正常写入被过滤的敏感词。创建失败有两个分支,一个是文件夹创建失败,会把用户名明文拿去和日志信息做拼接;一个是banned日志文件创建失败,直接写入一串信息,没有拼接。 最后将第二点走完的结果用log_action函数写到总日志里(这里又个很容易掉进去的陷阱,就是日志其实是有两份的,一个是总日志access.log,一个是banned日志。我们能作拼接任意写入操作的其实是总日志。) 到这里其实已经可以达到写入任意字符串的目的,但还不能说明什么,因为写入的地址并不能被访问,而且文件名是固定的,所以直接上马可能性不大,得找地方利用这个允许我们随意写东西的地方。 看回刚刚爆破开的admin.php,在登陆验证逻辑过后紧接着这一段 可以看到它把log文件读到变量$action_log,接着$log_lines再以换行作为分隔符将日志作切割。这里我们再看回到config.php,来看一下日志是怎么写的。 可以看到$log_file变量就是以换行为结尾的,并且以逗号为分割。所以我们继续往下看admin.php。可以发现我们写入的东西都会被处理完后放到对应的变量里,而只有$additional_info是我们可以控制的($encoded_user是base64,控制不了)。 接着往下看 这段代码将$encoded_user作为用户名拼接路径,然后再把路径的内容读出来。那么利用思路就清晰了,我们刚才一直在找的任意写入应该就是在这里发挥用处,也就是控制$encoded_user。但这不就和上面说的不能控制矛盾了吗?有什么办法可以通过$additional_info来控制$encoded_user吗? 如果你的思维足够跳跃,应该就能想到用任意文件写入来写入换行,以此在$additional_info来创建一条假日志。这样我们就能控制这条假日志的用户名了。说起来有点抽象,我们来看个具体的例子 这是根据log_action函数生成出来的日志,大概像下面这样 1,abwidab,admin_login,1,\n //log_action($username, 'admin_login', 1); 2,dqbvwwfqiq,register,1,\n //log_action($username, 'register', 1); 3,wqifbqwibc,record_banned,0,Failed to record banned content\n //当record_banned无法创建目录但可以创建文件时log_action($username, 'record_banned', 0, 'Failed to record banned content'); 4,wqifbqwibc,record_banned,0,Failed to create record directory for 这里拼接任意字符串\n //当record_banned无法创建目录但可以创建文件时log_action($username, 'record_banned', 0, 'Failed to create record directory for '.$username); 那么username应该输入什么呢?示例如下 2,dqbvwwfqiq,register,1,\n 4,wqifbqwibc,record_banned,0,Failed to create record directory for \n 11,../flag,record_banned,1,111\n 而我们输入的用户名就是 ...

March 18, 2025 · 1 min · Red

24-25下半学年第二次线下赛 | Writeup

只开了一台靶机。这里我以本地的环境为例简单讲一些重要的指令和过程 外网打点 除了回环地址和我的物理机外,设备有两台,.80和.201 用fscan扫一下 只能扫到.80,应该是.201有防火墙,那就从这台开始入手 重点放在服务上,会发现7001端口开了http。这个端口一般是weblogic,所以直接上工具(shack2/javaserializetools) 检查发现存在CVE2017,换个工具(sp4zcmd/WeblogicExploit-GUI)直接上冰蝎马 先确保能执行 然后直接上马。建议上完内存马再上一个webshell,因为实测起来内存马容易连不上 这里要注意冰蝎启动前先换到11或17,否则会出现连不上的情况(没有的话可以用apt install openjdk-17-dev安装) 这里我连webshell 成功上线 上来后选择虚拟终端先再命令执行页面执行whoami查看权限 目的是查看权限和确保马能正常使用。接着为了方便内网信息搜集,所以要上CS或者msf。这里我选择CS,但是如果直接上Beacon会被杀软干掉,所以我们要想办法绕过。一种办法是做免杀马,但比较麻烦,所以我们选择用CS的Web Drive-by绕 首先监听器是必须的,就不演示了 接着选择Attack->Web Drive-by->script web delivcery 点击Launch后会给你一段powershell的马,直接再冰蝎运行即可,等待十五秒左右就能上线 接着修改sleep,然后进行提权和内网信息搜集。 不过如果你想用msf内置的CVE拿meterpreter也是可以,只不过后面提权msf会遇到问题,还是得转到CS。如果你用的是msf,想转到CS,可以用下面的方法来 首先还是在CS弄好监听器, 接着将meterpreter放到background,接着按下面的步骤来设置 就能成功上线CS 内网横向 先提权,SVC即可 接着关防火墙,开3389 netsh firewall set opmode disable REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f 接着扫目标,可以用portscan也可以用net view(查看域存活主机)。当然要是你想上fscan也可以,只不过有360所以被干掉的概率比较大 就能看到10段的内网靶机列表。不知道怎么进入这个列表去看第一场wp 当然,如果你想看是否有360,只需要点击下port scan下面的process list就可以看到 ...

March 14, 2025 · 1 min · Red

24-25下半学年第一次线下赛 | Writeup

取证赛道 计算机、U盘、APK、手机 2024数证杯初赛 - WXjzc - 博客园 服务器 服务器1 直接用md5sum算出即可:a2444b61b65be96fc2e65924dee8febd 服务器2 直接用strings就能梭出来 strings ubuntu-client-Snapshot2.vmem | grep ubuntu-client 服务器3 通过上一题可以发现两个接口/connect和/upload/ 尝试在GDB中运行ubuntu-client,但缺少libcrypto.so.1.1库 使用以下命令下载并编译缺失的库: wget -c https://www.openssl.org/source/openssl-1.1.1s.tar.gz && \ tar xf openssl-1.1.1s.tar.gz && \ cd openssl-1.1.1s/ && \ ./config --prefix="/usr/local/openssl" && \ make 使用环境变量LD_LIBRARY_PATH指定GDB查找依赖库的路径: LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/michal/openssl-1.1.1s gdb ubuntu-client 使用GDB扩展peda来辅助分析,用info functions命令列出恶意软件使用的函数,注意到curl_easy_setopt@plt,这是一个用于建立curl连接的函数 在curl_easy_setopt函数上设置断点: b curl_easy_setopt 开始调试: run xGonnaGiveIt2Ya 就能发现地址 服务器4 根据上一题,可以直接得出:https://plankton-app-3qigq.ondigitalocean.app/upload/ 服务器5 因为提供的内存转储文件无法被Volatility正确处理,所以要先确定生成转储文件的机器所使用的内核版本。 通过运行命令python3 vol.py -f ../ubuntu-client-Snapshot2.vmem banners,可以确定Ubuntu使用的内核版本是5.4.0-163-generic 接着使用这个仓库,并将其内容复制到volatility3/symbols/linux目录中,然后查找与执行命令ubuntu-client xGonnaGiveIt2Ya时间对应的.service文件 ...

March 10, 2025 · 2 min · Red