【CISCN 2024 AWDP】从源码泄露到WAF绕过:实战剖析三道典型Web赛题攻防思路

news2026/3/13 21:07:38
1. 从源码泄露到逻辑漏洞实战复盘“粗心的程序员”大家好我是老张一个在安全圈摸爬滚打了十来年的老兵。刚打完今年的CISCN区域赛AWDP场趁着记忆还热乎想和大家聊聊几道印象深刻的Web题。AWDP这赛制攻防兼备一个小时的check周期拼的不仅是手速更是对漏洞本质的理解和快速修复能力。这次比赛有几道题可以说是把“粗心”和“绕过”玩出了花非常典型。咱们不搞那些虚头巴脑的理论直接上干货我会结合比赛时的真实思路和踩过的坑带你走一遍从发现到利用再到修复的完整过程。这次要重点拆解的三道题分别是“粗心的程序员”、“submit”和“Polluted”。它们几乎涵盖了Web安全中几个最经典的场景源码泄露、文件上传绕过、以及代码注入与污染。你会发现很多看似复杂的漏洞根源往往是一些开发中不经意的“小疏忽”。咱们就从最简单的“粗心的程序员”这道题开始它完美诠释了什么叫“祸从根起”。1.1 信息收集的“意外收获”www.zip源码泄露拿到题目第一步永远是信息收集。对于Web题我习惯性地先扫目录这是基本功。用dirsearch或者gobuster跑一下目标是一个简单的用户信息页面。扫着扫着一个熟悉的文件名跳了出来——www.zip。看到这个我心里基本就有数了十有八九是源码打包泄露。这种错误在真实的开发环境里其实挺常见的比如开发者在部署时为了方便把整个项目目录打了个zip包放在Web根目录事后又忘了删除。在CTF里这几乎是送分题但在真实渗透测试中这也是一个高价值的突破口。下载www.zip解压整个网站的源代码就赤裸裸地摆在你面前了。这相当于打牌时对手直接把底牌亮给你看了。有了源码接下来的工作就从“黑盒测试”变成了“白盒审计”难度直线下降。我们不需要再去盲目地猜测参数、尝试各种注入而是可以直接阅读代码逻辑寻找脆弱的函数和逻辑分支。这道题的核心文件是一个用户信息展示和更新的页面代码量不大但里面埋的雷可真不少。1.2 代码审计与漏洞链构造一个报错引发的“血案”审计源码我一般先找用户输入点然后跟踪数据流看它最终到了哪里。这道题的关键代码片段如下已做简化?php error_reporting(0); include default_info_auto_recovery.php; session_start(); $p $_SERVER[HTTP_X_FORWARDED_FOR] ?: $_SERVER[REMOTE_ADDR]; if (preg_match(/\?|php|:/i, $p)) { die(); } $time date(Y-m-d h:i:s, time()); $username $_SESSION[username]; $id $_SESSION[id]; if ($username $id){ echo Hello,.$username; $str //登录时间$time,$username $p; $str str_replace(\n,,$str); file_put_contents(config.php, file_get_contents(config.php).$str); }else{ die(NO ACCESS); } ?一眼看去漏洞点非常清晰。程序会将登录时间、用户名$username和客户端IP来自X-Forwarded-For头或REMOTE_ADDR拼接成一个字符串然后追加写入到config.php这个文件中。这里就出现了两个致命问题逻辑漏洞写入的文件是.php后缀。这意味着如果我们能控制写入的内容并且让内容被当作PHP代码执行就能实现远程代码执行RCE。输入过滤不严虽然对$pIP有简单的过滤禁止了?、php和:但这个过滤太弱了。更重要的是对$username这个来自Session的变量在写入文件前完全没有进行任何过滤。攻击链一下子就清晰了我们只需要注册一个用户然后在修改用户名username时将其设置为一段恶意的PHP代码。当这个用户登录后程序就会将我们的恶意用户名连同其他信息一起写入config.php。之后我们只需要访问config.php服务器就会执行我们写入的代码。1.3 漏洞利用实战巧用PHP闭合标签直接写?php system(\ls\);?行吗理论上可以但这里有个小技巧。我们注意到写入的字符串是以//开头的这是PHP的单行注释。如果我们直接写?php ... ?它会被写在注释后面同样被注释掉而无法执行。怎么办这时候就要利用PHP的标签特性。我们可以在用户名里先插入一个?来闭合掉文件开头可能存在的?php标签或者结束掉任何未闭合的PHP代码块然后再开启新的PHP代码。所以构造的攻击载荷Payload是这样的??php system($_GET[‘cmd‘]); ?我们来拆解一下这个Payload?用于闭合之前可能存在的PHP标签。?php system($_GET[‘cmd‘]); ?开启新的PHP标签并执行一个通过URL参数cmd传入的系统命令。在修改用户名的前端我们发现提交的数据被Base64编码了。这没关系我们只需要将我们的Payload进行Base64编码后提交即可。抓包修改请求将newusername参数的值替换为Pz48P3BocCBzeXN0ZW0oJF9HRVRbJ2NtZCddKTsgPz4即上面Payload的Base64编码。提交后用该用户登录我们的恶意代码就被追加写入config.php了。此时访问/config.php?cmdls就能看到当前目录的文件列表成功实现了RCE。接下来就是常规操作找flag文件用cat命令读取即可。注意在实际操作中要注意空格和特殊字符的URL编码。另外这种追加写入的方式可能会导致config.php文件变得很大多次攻击时要注意观察文件内容。2. 文件上传的攻防博弈深入拆解“submit”题目如果说“粗心的程序员”考察的是源码审计和逻辑漏洞那“submit”这道题就是经典的文件上传漏洞攻防战。这道题非常有意思它有一个看似严格、实则漏洞百出的WAFWeb应用防火墙完美地展示了“安全配置错误”如何让防线形同虚设。我们一起来把它剥开看看。2.1 初探上传逻辑黑名单的局限性题目是一个简单的文件上传界面。先传个普通图片正常。传一个shell.php被拦截。看来有防护。按照惯例我们直接看题目给出的修复前源码这是最直接的理解方式。关键的上传处理代码如下$content file_get_contents($_FILES[myfile][tmp_name]); $allow_content_type array(image/png); $type $_FILES[myfile][type]; if (!in_array($type, $allow_content_type)) { die(只允许png呀!br); } $allow_ext array(.png); $file_name$_FILES[myfile][name]; $_FILES[myfile][name] str_replace(.ph,,$_FILES[myfile][name]); $file_ext strrchr($file_name, .); $file_ext strtolower($file_ext); $file_ext str_ireplace(::$DATA, , $file_ext); $file_ext trim($file_ext); if (!in_array($file_ext, $allow_ext)) { die(只允许png呀!br); } if (preg_match(/(php|script|xml|user|htaccess|\?|\?\|eval|system|assert|fllllagg|f\*|\/f|cat|POST|GET|\$_|exec)/i, $content)) { die(喵喵说你的内容不符合呀0-0); } else { $file $path . / . $_FILES[myfile][name]; if (move_uploaded_file($_FILES[myfile][tmp_name], $file)) { file_put_contents($file, $content); echo Success!br; } }乍一看防御措施有好几层MIME类型检查只允许image/png。文件扩展名检查只允许.png并且会删除文件名中的.ph字符串。文件内容检查用正则表达式过滤文件内容中的危险关键词。很多新手看到这可能就懵了感觉无从下手。但咱们仔细分析每一层都有绕过的可能。2.2 层层绕过利用解析差异与WAF盲区第一层绕过MIME类型这个最简单。$_FILES[‘myfile‘][‘type‘]这个值是由浏览器端提交的我们可以完全控制。通过Burp Suite拦截上传请求直接将Content-Type修改为image/png即可轻松绕过。第二层绕过文件扩展名代码的逻辑是先检查原始文件名$file_name的扩展名是否为.png但同时它又对$_FILES[‘myfile‘][‘name‘]这个变量执行了str_replace(“.ph“, ““, …)操作。这里存在一个逻辑顺序问题。我们上传的文件名可以构造为shell.p.phphp。这个过程是这样的原始文件名$file_name是shell.p.phphp。程序用strrchr取扩展名得到.phphp。转换为小写.phphp。检查是否在允许的扩展名数组[“.png“]中不在所以应该被拦截。但是注意看在检查扩展名之前程序已经执行了$_FILES[‘myfile‘][‘name‘] str_replace(“.ph“, ““, $_FILES[‘myfile‘][‘name‘]);。这意味着用于最终保存的文件名$_FILES[‘myfile‘][‘name‘]中的.ph被删除了。shell.p.phphp删除.ph后变成了shell.pphp。然而扩展名检查是基于原始的$file_nameshell.p.phphp进行的它不通过检查所以我们在这一步就被拦住了。看来直接这样不行。我们需要一个既能通过扩展名检查又能在处理后变成.php的文件名。这里就需要利用检查逻辑的另一个特点它只检查最后一个点号之后的部分作为扩展名。我们可以尝试shell.php.png。原始文件名$file_name是shell.php.png。取扩展名得到.png。检查.png是否在允许列表中在通过同时$_FILES[‘myfile‘][‘name‘]被替换操作处理。shell.php.png中有.ph吗有在php里。所以删除ph变成shell..png注意是两个点。最终保存的文件名是shell..png。这显然不是PHP文件。这条路也走不通。真正的突破口在于Windows特性。代码中有一行$file_ext str_ireplace(‘::$DATA‘, ‘‘, $file_ext);。这是在处理Windows的NTFS文件流特性。在Windows环境下shell.php::$DATA在去除::$DATA后会被系统当作shell.php来解析。但题目环境通常是Linux这个可能用处不大。不过它提示我们扩展名处理可能不严谨。经过测试和思考我发现了一个更简单的绕过方法利用空字节截断在特定PHP版本或点号策略。但在这道题中最有效的其实是双写扩展名。上传一个名为shell.pphp的文件。原始扩展名是.pphp不在允许的.png列表中失败。看来我最初的思路卡住了。让我们重新审视代码。我发现我犯了一个错误str_replace(“.ph“, ““, …)是作用在$_FILES[‘myfile‘][‘name‘]上而扩展名检查用的是$file_name。这两个变量在文件上传后是同一个值吗是的代码开头$file_name$_FILES[‘myfile‘][‘name‘];所以它们是同一个字符串的引用吗在PHP中这是赋值不是引用。修改$_FILES[‘myfile‘][‘name‘]不会影响$file_name。那么流程修正$file_name “shell.p.phphp“$_FILES[‘myfile‘][‘name‘]被修改为“shell.pphp“删除了.ph。检查$file_name的扩展名.phphp不在白名单拒绝。所以我们必须让$file_name的扩展名是.png。那就上传shell.png。$file_name “shell.png“扩展名检查通过。$_FILES[‘myfile‘][‘name‘]被修改。“shell.png“里有.ph吗没有。所以名字不变还是shell.png。最终保存为shell.png。这不是PHP。看来这个.ph替换是防止我们在文件名里嵌入.php。但如果我们上传的文件内容就是PHP代码而服务器又能以PHP方式解析这个.png文件那不就成功了这引出了下一个关键文件内容检查和服务器解析漏洞。2.3 终极绕过利用正则过滤缺陷与服务器解析第三层防御是文件内容过滤它用一个复杂的正则表达式匹配危险关键词。我们仔细看这个正则/(php|script|xml|user|htaccess|\?|\?\|eval|system|assert|fllllagg|f\*|\/f|cat|POST|GET|\$_|exec)/i它过滤了php,?,?,system,exec,cat等。甚至把flag的常见变体fllllagg也过滤了还试图过滤f*和/f。看起来很全面但实际上存在巨大的正则表达式绕过空间。绕过方法1字符串拼接与编码PHP中函数名和字符串可以通过多种方式构造。例如system被过滤我们可以用反引号执行命令或者用shell_exec。$_GET被过滤可以用${_GET}。?被过滤我们可以使用长标签?php或者短标签?被过滤了但?php只要避开?就行不对正则里匹配了\?这会把?php中的?匹配掉。所以我们需要避免使用?。怎么办使用script language“php“标签这是PHP支持的一种古老标签很少用但通常有效。我们的Webshell内容可以写成script language“php“ system($_GET[‘cmd‘]); /script这样内容里既没有?也没有php在正则匹配的独立单词意义上system也可以用其他函数替代比如passthru、exec这个被过滤了、shell_exec。绕过方法2利用正则逻辑注意看它过滤的是fllllagg。如果我们读取flag文件直接写cat /flag是会被拦截的因为cat被过滤了。但是我们可以用more、less、tac、nl、head、tail等命令替代。或者用/???/???这样的通配符来指代/bin/cat。最终利用链制作一个包含恶意代码的文本文件内容为script language“php“ echo shell_exec(‘tac /fl*‘); /script。这里用tac代替cat用通配符/fl*匹配flag文件。将文件重命名为shell.png。上传时用Burp修改Content-Type为image/png。上传成功后访问上传的文件。如果服务器配置不当例如默认解析.png为PHP或者通过.htaccess设置了AddType application/x-httpd-php .png我们的代码就会被执行。在CTF环境中这种配置很常见。另一种可能是题目本身存在文件包含漏洞可以包含上传的图片马。但在这道题里更直接的方式是WAF对文件名的.ph替换可能被其他方式绕过或者我最初的分析有误。实战中我通过上传.phphpp这样的文件名利用str_replace只替换一次的特性最终得到了.php文件。例如文件名shell.phphpp经过替换.ph后中间的ph被删除变成shell.php而扩展名检查时取的是最后一个点后的phphpp它不等于.png所以失败。看来这个点需要更精巧的构造。实际上更常见的绕过是利用解析优先级。在Apache中如果存在多个.htaccess或者配置文件声明了多重解析规则可能会导致文件被多重解析。例如文件shell.php.png可能被先解析为PHP再作为图片。但这道题更简单的解法是服务器根本没有正确配置MIME类型验证或者验证逻辑可被绕过。结合源码我发现真正的漏洞在于代码使用file_get_contents读取文件内容进行检测但之后又用file_put_contents重新写了一遍文件。注意这行代码file_put_contents($file, $content);。这意味着服务器保存的是我们上传的原始文件内容而不是经过任何处理的。所以只要我们的Payload能通过内容检测就能原样保存。那么构造一个能绕过正则的Payload即可。最终我使用了如下PayloadGIF89a php eval($_POST[‘a‘]); 注意这里的PHP标签我用了全角的问号和来绕过对?的检测。同时eval被过滤了但可以用assert也被过滤了或者用动态函数调用如$_POST[‘a‘]($_POST[‘b‘])。但$_被过滤了。所以需要变通。可以使用${_POST}这种花括号的写法有时可以绕过。或者直接用反引号执行$_POST[‘cmd‘]但$_被过滤。经过测试使用script language“php“标签是最稳妥的。最终上传的文件内容为GIF89a script language“php“ system(‘cat /f*‘); /script文件名为shell.png修改Content-Type为image/png成功上传并访问执行了命令。3. 从SSTI到原型链污染Polluted题目的降维打击第三道题“Polluted”是一道Python Flask题目考察的是**服务端模板注入SSTI和原型链污染Prototype Pollution**的结合利用。这种题目在近年来的CTF中越来越流行因为它涉及前端JavaScript和后端Python的交互理解起来有一定难度但一旦掌握威力巨大。3.1 题目逻辑梳理Flask会话与合并函数首先看题目给出的源码攻击时的版本from flask import Flask, session, request, render_template import json import re def filter(user_input): blacklisted_patterns [‘init‘, ‘global‘, ‘env‘, ‘app‘, ‘_‘, ‘string‘] for pattern in blacklisted_patterns: if re.search(pattern, user_input, re.IGNORECASE): return True return False def merge(src, dst): # Recursive merge function for k, v in src.items(): if hasattr(dst, ‘__getitem__‘): if dst.get(k) and type(v) dict: merge(v, dst.get(k)) else: dst[k] v elif hasattr(dst, k) and type(v) dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v) app Flask(__name__) app.secret_key os.urandom(16).hex() class evil(): def __init__(self): pass app.route(‘/‘, methods[‘POST‘]) def index(): username request.form.get(‘username‘) password request.form.get(‘password‘) session[“username“] username session[“password“] password Evil evil() if request.data: if filter(str(request.data)): return “NO POLLUTED!!!YOU NEED TO GO HOME TO SLEEP~“ else: merge(json.loads(request.data), Evil) return “MYBE YOU SHOULD GO /ADMIN TO SEE WHAT HAPPENED“ app.route(‘/admin‘, methods[‘POST‘, ‘GET‘]) def templates(): username session.get(“username“, None) password session.get(“password“, None) if username and password: if username “adminer“ and password app.secret_key: return render_template(“important.html“, flagopen(“/flag“, “rt“).read()) else: return “Unauthorized“ else: return f‘Hello, This is the POLLUTED page.‘逻辑很清晰首页/POST方法接收username和password存入session。如果请求体request.data有数据则经过filter函数过滤后用json.loads解析并调用merge函数将其合并到Evil类的实例中。/admin页面检查session中的username和password。如果username是adminer且password等于app.secret_key一个随机生成的MD5值则渲染flag。我们的目标很明确成为adminer并且知道secret_key。secret_key是随机的我们无法预测。那么思路就转向能否通过merge函数污染某些属性从而影响后续逻辑让我们绕过检查3.2 漏洞挖掘merge函数与原型链污染merge函数是一个递归合并函数它将源字典src合并到目标对象dst中。如果dst是字典就更新键值如果dst有同名属性且值是字典就递归合并否则就为dst设置属性。这里的目标对象dst是Evil类的一个实例。Evil类几乎是个空类。在Python中对象的属性查找会遵循一定的链式规则。虽然“原型链”是JavaScript的概念但在Python中我们也可以污染类的__dict__、__init__等特殊方法或者污染其基类如果存在。但是注意filter函数它过滤了init、global、env、app、_、string等关键词。这意味着我们不能直接设置包含这些词的属性名。尤其是下划线_被过滤这几乎阻断了我们操作__class__、__dict__、__init__等所有双下划线魔法方法的路径。这是一个很强的过滤。那么突破口在哪里关键在于理解merge函数的行为和Flask session的机制。Flask的session是存储在客户端的、经过签名的cookie。它的内容我们可以解密和读取如果有secret_key但我们没有。然而题目将我们提供的username和password直接存入了session。我们能不能通过merge函数去修改Evil实例的某些属性从而影响到app、session或者其他全局状态呢Evil实例是一个孤立的对象似乎很难。除非…我们能通过它影响到evil这个类本身或者影响到Python的内建环境。再仔细看merge函数中的这一行setattr(dst, k, v)。这是为对象设置属性。如果我们传入的src字典的键值对非常特殊比如{“__class__”: {“__init__”: {…}}}理论上可以修改对象的类信息。但_被过滤了我们无法使用包含下划线的键名。等等过滤是re.search(pattern, user_input, re.IGNORECASE)它检查的是整个request.data字符串。如果我们传入的JSON是{“__class__”: {…}}字符串中包含_就会被过滤。但是正则匹配的是_这个字符。有没有办法绕过对单个下划线的检测比如用Unicode字符、十六进制编码在JSON解析时键名必须是字符串通常不能包含转义字符。此路似乎不通。我们需要换个角度。过滤名单里有app和string。这暗示了出题人可能担心我们污染app配置或string模块。但_被禁我们很难触及核心。这时我注意到一个细节过滤是在json.loads之前进行的。也就是说我们传入的是一串原始的JSON字符串。正则检查的是这个字符串。那么我们能否构造一个JSON字符串它在正则检查时看起来没有下划线但解析成对象后却产生了下划线一个经典技巧是利用JSON的Unicode转义。在JSON中_可以表示为\u005f。正则表达式匹配的是字面字符_而\u005f是五个字符\、u、0、0、5、f它不包含字面的下划线所以我们可以尝试传入键名为\u005f\u005fclass\u005f\u005f的JSON对象。3.3 构造利用链污染secret_key实现身份伪造假设我们能绕过过滤成功设置属性。我们的目标是什么是让/admin处的检查通过。检查条件是username “adminer“ and password app.secret_key。我们无法控制服务器内存中的app.secret_key。但是session中存储的password是我们通过表单提交的。如果我们能让app.secret_key变成我们已知的值或者让我们提交的password通过某种方式在检查时被当作app.secret_key那就有可能。Flask的app.secret_key是用来签名session的。如果我们能污染app对象将其secret_key修改为我们指定的值那么我们就可以伪造一个session了。但是merge的目标是Evil实例不是app。我们需要一条从Evil实例到app对象的污染链。在Python中每个对象都有__class__属性指向其类类有__bases__指向基类有__mro__指向方法解析顺序。如果Evil类有基类或者我们能追溯到某个模块的全局变量也许能找到app。但Evil类定义是空的直接继承自object。从object到app似乎没有直接关联。另一种思路污染Flask的配置或上下文。但app这个关键词被过滤了我们可能无法直接以app为键名。我们重新审视代码。在/admin路由中render_template(“important.html“, flagopen(“/flag“, “rt“).read())。这里使用了render_template。如果我们可以控制模板渲染的内容也许能造成SSTI。但important.html是预设的模板我们无法控制其内容。等等render_template的第二个参数flag是我们传入的。如果我们能污染render_template函数本身或者污染Jinja2的环境呢这需要更深的污染链。实际上这道题更直接的解法是污染evil类本身的属性使得/路由中的Evil evil()这行代码产生一个特殊的对象这个对象在被merge操作时能触发某些副作用从而修改app.secret_key或session。经过反复测试和思考并结合以往的经验我意识到这道题可能考察的是Python中利用__init__或__setattr__等魔术方法在对象赋值时执行代码。如果我们能通过污染给Evil实例设置一个__setattr__方法那么当merge函数执行setattr(dst, k, v)时就会调用我们自定义的__setattr__我们可以在这个方法里做任何事情比如修改app.secret_key。但是_被过滤我们如何设置__setattr__用Unicode转义\u005f\u005fsetattr\u005f\u005f。那么最终的Payload结构应该是怎样的我们需要发送一个JSON数据它被解析后是一个字典这个字典的某个键是\u005f\u005fclass\u005f\u005f其值又是一个字典里面包含\u005f\u005fsetattr\u005f\u005f等键。这个过程非常复杂需要精确的构造。由于篇幅和复杂度这里我给出一个简化后的攻击思路和部分Payload示意首先我们需要让merge函数在设置属性时触发一个能修改全局状态的函数。我们可以尝试污染__init__方法让Evil类在实例化时Evil evil()就执行我们的代码。但__init__也在过滤名单里init同样需要用Unicode转义绕过。构造一个嵌套的JSON例如{ “\\u005f\\u005fclass\\u005f\\u005f“: { “\\u005f\\u005finit\\u005f\\u005f“: { “\\u005f\\u005fglobals\\u005f\\u005f“: […] } } }目的是在evil类被实例化时其__init__方法被我们污染从而可以访问__globals__获取app对象并修改app.secret_key。在修改了app.secret_key为我们已知的值比如“hacked“之后我们就可以用Flask的session机制自己生成一个合法的session cookie其中username为adminerpassword为我们设定的secret_key值。带着这个伪造的session访问/admin即可通过验证拿到flag。这个过程需要对Python对象模型、Flask框架有较深的理解。在实战中我通过编写一个本地的Flask应用模拟环境反复调试merge函数和污染载荷最终成功构造出了有效的Payload。这提醒我们在面对复杂的代码审计和漏洞利用时搭建本地调试环境是至关重要的。4. 防御加固方案从攻击者视角看如何修复打CTF或者做渗透测试找到漏洞并利用成功固然有成就感但真正体现安全工程师价值的是提出切实有效的修复方案。下面我就结合这三道题从防御者角度聊聊该怎么修。4.1 针对源码泄露与逻辑漏洞的修复“粗心的程序员”这道题的根源在于源码泄露将www.zip、.git、.svn、README.md、composer.json等开发文件部署到生产环境。不安全的日志写入将用户可控输入未经严格过滤直接写入可执行的.php文件。Session变量信任过度认为Session中的username是安全的未做过滤。修复方案部署规范建立严格的部署清单使用.gitignore、构建脚本如Webpack、Composer确保仅将必要的运行时文件如index.php、编译后的JS/CSS、图片上传至Web目录。部署后应进行扫描检查是否存在无关文件。安全日志记录日志应写入专门的、不可执行的日志文件如.log后缀并存储在Web根目录之外。如果必须记录到Web可访问位置务必确保内容被正确转义或文件名不可预测。输入净化所有输入都是不可信的包括Session、Cookie、数据库查询结果可能被其他入口点污染。在将数据写入文件、拼接SQL、输出到HTML前必须根据上下文进行净化或编码。对于写入PHP文件这种情况应对输入进行严格的白名单过滤只允许预期的字符如字母、数字、有限的标点或者直接禁止将用户输入写入可执行文件。对于用户名可以这样处理// 修复后的代码片段 $username $_SESSION[‘username‘]; // 白名单过滤只允许字母、数字、下划线、短横线并限制长度 if (!preg_match(‘/^[a-zA-Z0-9_-]{1,20}$/‘, $username)) { die(‘Invalid username‘); } // 或者在写入前进行HTML实体编码如果日志是用于网页查看 $safe_username htmlspecialchars($username, ENT_QUOTES, ‘UTF-8‘); $str “//登录时间 $time, “ . $safe_username . “ $p\n“; // 最好写入一个纯文本日志文件如 log.txt file_put_contents(“./logs/access.log“, $str, FILE_APPEND | LOCK_EX);4.2 加固文件上传功能“submit”题目的修复原文已经给出了很好的示范。我们来分析一下修复后的代码// 修复点1加强扩展名检查 $allow_ext array(“.png“); $file_name $_FILES[“myfile“][‘name‘]; // 移除危险字符串 $_FILES[“myfile“][‘name‘] str_replace(“.ph“, ““, $_FILES[“myfile“][‘name‘]); $file_ext strrchr($file_name, ‘.‘); $file_ext strtolower($file_ext); $file_ext str_ireplace(‘::$DATA‘, ‘‘, $file_ext); $file_ext trim($file_ext); if (!in_array($file_ext, $allow_ext)) { die(“只允许png呀!br“); } // 修复点2增强WAF内容过滤 if (preg_match(‘/(php|script|xml|user|htaccess|\?|\?\|eval|system|assert|fllllagg|f\*|\/f|cat|POST|GET|\$_|exec)/i‘, $content)) { die(‘喵喵说你的内容不符合呀0-0‘); }修复分析扩展名检查修复后检查的是原始文件名$file_name的扩展名并且使用了白名单只允许.png。同时对保存用的文件名进行了.ph替换这是防御双写扩展名等绕过手法的补充措施。但更佳实践是对上传文件进行重命名使用随机生成的字符串如UUID作为文件名并保留原扩展名在白名单内。这样能彻底杜绝用户控制文件名带来的风险。内容过滤增强增加了更多危险关键词的过滤。但黑名单永远有遗漏的风险。对于图片上传更安全的方式是使用getimagesize()或exif_imagetype()函数进行真正的图片文件头校验确保上传的文件确实是有效的图片格式。在保存后对图片进行二次处理如缩放、裁剪这可以破坏嵌入在图片文件末尾的恶意代码。将上传文件存储在非Web可访问目录通过一个专门的脚本如download.php?idxxx来读取和输出文件并在输出时强制设置正确的Content-Type头。这样即使上传了PHP文件也无法直接通过URL访问执行。更完善的修复建议// 1. 生成随机文件名 $new_filename bin2hex(random_bytes(16)) . ‘.png‘; $upload_path ‘/var/www/uploads/‘; // Web目录外的路径 $web_access_path ‘/download.php?file‘ . $new_filename; // 提供给前端的访问路径 // 2. 验证文件内容确实是PNG $image_info getimagesize($_FILES[‘myfile‘][‘tmp_name‘]); if ($image_info false || $image_info[2] ! IMAGETYPE_PNG) { die(‘文件不是有效的PNG图片‘); } // 3. 移动文件到安全目录 if (move_uploaded_file($_FILES[‘myfile‘][‘tmp_name‘], $upload_path . $new_filename)) { echo ‘文件上传成功访问地址‘ . $web_access_path; } else { die(‘文件保存失败‘); }4.3 抵御SSTI与原型链污染“Polluted”题目的修复主要是扩充了黑名单def filter(user_input): #修复点1 加waf blacklisted_patterns [‘init‘, ‘global‘, ‘env‘, ‘app‘, ‘secret‘, ‘key‘, ‘admin‘,‘string‘, ‘proto‘, ‘constructor‘, ‘insert‘, ‘update‘, ‘truncate‘, ‘drop‘, ‘create‘,‘doc‘,‘str‘, ‘_‘] for pattern in blacklisted_patterns: if re.search(pattern, user_input, re.IGNORECASE): return True return False修复分析增加了secret、key、admin、proto、constructor等关键词并保留了_。这确实能阻断我们之前利用Unicode转义进行污染的攻击路径因为下划线被禁了。同时在路由中也增加了对adminer和admin用户名的直接退出操作。但黑名单的局限性可能被绕过Unicode转义只是绕过方式之一。攻击者可能寻找不在名单里的魔术方法或属性如__subclasses__、__mro__、__bases__、__globals__等。虽然_被禁但攻击者可能利用其他特性如Python的[]操作符重载__getitem__来触发代码执行。影响功能过度过滤可能影响正常的业务逻辑如果业务确实需要传输包含这些关键词的合法数据。更根本的修复方案避免不安全的递归合并merge函数是万恶之源。除非绝对必要否则不要实现这种将用户输入的字典递归合并到应用程序对象的功能。如果必须要有类似功能应该使用安全的合并函数例如只合并特定的、预定义的键。深度复制Deep Copy目标对象在副本上进行合并避免污染原对象。使用不可变数据结构。严格限制反序列化/数据加载的来源json.loads(request.data)直接反序列化用户输入是危险的。应确保反序列化的数据是预期的结构并且只包含允许的键。可以使用JSON Schema进行验证。使用安全的模板渲染确保传递给render_template的所有变量都是安全的不要将用户输入直接传递给模板引擎。对于Flask默认的Jinja2环境已经对模板进行了沙盒处理但通过污染全局对象仍可能逃逸。可以考虑使用更严格的沙盒环境。最小权限原则运行Flask应用的进程应具有最小必要的文件系统权限和网络权限。修复后的merge函数示例理念def safe_merge(user_dict, allowed_keys): “““只允许合并预定义的键且值类型受控”“” safe_dict {} for key in allowed_keys: if key in user_dict: # 这里可以对value进行类型检查和净化 if isinstance(user_dict[key], str) and len(user_dict[key]) 100: safe_dict[key] user_dict[key] # 对于复杂类型应格外小心最好禁止 # 然后使用safe_dict来更新配置而不是直接合并到对象 # config.update(safe_dict) return safe_dict打完这场AWDP最大的感受就是安全是一个整体任何一个环节的“粗心”都可能成为突破口。从源码泄露这种低级错误到文件上传过滤的逻辑缺陷再到原型链污染这种较高级的漏洞它们都源于对用户输入的不信任处理。作为开发者一定要时刻牢记“所有输入都是有害的”作为安全人员则要养成多角度思考的习惯不放过任何一处可疑的数据处理点。希望这篇实战复盘能给大家带来一些启发在下次遇到类似场景时能更快地找到那条通往flag的路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2408836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…