ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

bugku-awd-3s-6

bugku-awd-3s-6 bugku-awd-3s-6一、MySQL 3306 对外 cms 固定弱口令 ALL PRIVILEGES根因漏洞类型数据库弱口令 权限配置不当。这场最致命的一个赛后复盘才彻底搞明白。形成原因所有靶机的core/config.php里MySQL 密码是同一个固定值eb0d39d2931c480f而且cms用户是cms%——任意主机都能连权限还是ALL PRIVILEGES ON *.* WITH GRANT OPTION。MySQL 又开了bind_address*3306 端口直接暴露在公网。换句话说知道这个固定密码的人就能直连的我们数据库干任何事。14:40 对手就是这么打进来的直连 3306 →SET GLOBAL general_log把 webshell 写到/var/www/html/cache/sh96.php→ 顺手把mimelist改成放行 .php → 再改掉 admin 密码 → 14:59 用改后的密码登后台传了第二个 webshell。一开始还以为是 Web 注入后来翻日志、看文件 owner是mysql:mysql才确认是直连数据库。原代码截图源码?php/* 数据库配置 */$_L[mysql][host]127.0.0.1;$_L[mysql][port]3306;$_L[mysql][user]cms;$_L[mysql][pass]eb0d39d2931c480f;// ← 第 6 行固定弱口令$_L[mysql][name]cms;$_L[mysql][pre]lcms_;$_L[mysql][charset]utf8mb4;修改后的代码config.php同步成新密码fix_mysql.py干的活$_L[mysql][pass]cms_Awd_2026#Xy9k;数据库侧两步fix_mysql.py 漏洞总结 13.3-- 1. 改 cms 密码让旧固定密码失效ALTERUSERcms%IDENTIFIEDBYcms_Awd_2026#Xy9k;FLUSHPRIVILEGES;-- 2. 收紧权限就算再被直连也写不了文件、改不了全局变量REVOKESUPER,FILE,GRANTOPTIONON*.*FROMcms%;小记bind_address*其实改不掉要 root 重启 mysqld我们没 root所以只能靠「改密码 收权限」兜住。改完我用旧密码试连已经进不去了web 功能也正常才放下心。二、index.php 一句话后门漏洞类型Webshell 后门 / 远程命令执行RCE形成原因靶机根目录index.php第一行就躺着一句话木马谁 POST 个a参数都能执行任意 PHP 代码。三、upload 目录一句话木马漏洞类型Webshell 后门 / 远程命令执行RCE形成原因upload目录里还藏了一个一句话木马文件名是随机的看着像 base64POSTaaa就能 RCE。复现Burp Suite同样把请求丢进Repeater请求行改成POST /upload/0/file/202210/TYZMTYNTIxMzYMTAODcOQ.php HTTP/1.1Body 填aaasystem(cat /flag);点Go响应里直接回显/flag内容跟上面 index.php 那个一样好使。四、后台弱口令 admin/admin 4 位数字验证码漏洞类型弱口令 / 验证码可爆破形成原因后台超级管理员是admin密码存的是 MD5而且就是MD5(admin)。验证码更坑4 位纯数字randstr(4,num)存 sessionLCMSPINCODE输错还不重置——意味着对着同一张验证码能把 0000~9999 全爆一遍。对手 14:59 那波就是这么干的先用改后的密码登后台验证码暴力过掉然后走 upload 传了第二个 webshell。原代码截图源码-- lcms_admin 表里的 admin 行INSERTINTOlcms_adminVALUES(1,0,1,admin,超级管理员,21232f297a57a5a743894a0e4a801fc3,...);-- 21232f297a57a5a743894a0e4a801fc3 MD5(admin)修改后的代码改密码比赛里fix_upload_attack.py先把被篡改的密码恢复成原强密码赛后应该再设一个更复杂的UPDATElcms_adminSETpassa7fa6b25198e06dbb5c40dae058bd283WHEREnameadmin;五、login docheck SQL 注入登录绕过漏洞类型SQL 注入字符串拼接admin 模块 filterform 完全不过滤形成原因登录校验的docheck()里查询是拿str_replace把:name占位符替换进 where 子句的本质还是字符串拼接name参数没做任何过滤。所以name OR 11 --pass1就能直接绕过去。而且 admin 模块的filterform完全不过滤union select 都能回显配合验证码爆破基本等于白给。原代码截图源码publicfunctiondocheck(){global$_L,$CFGA;...$passmd5($_L[form][pass]);$admininfosql_get([admin,(name :name OR email :name OR mobile :name) AND pass {$pass},id DESC,[:name$_L[form][name],// ← 没过滤就直接替换进 SQL]]);修改后的代码给name加了个addslashesdeploy_waf.py里顺手改的$passmd5($_L[form][pass]);$nameaddslashes($_L[form][name]);$admininfosql_get([admin,(name :name OR email :name OR mobile :name) AND pass {$pass},id DESC,[:name$name,]]);六、后台登录绕过nlogin 重复参数漏洞类型认证绕过参数解析不一致形成原因这个藏得挺深。check_login()判断「是不是登录页」用的是HTTP_QUERY整个 REQUEST_URI 的子串匹配而真正要加载的模块名L_NAME是从$_GET[n]取的——PHP 对重复参数取的是最后一个。所以 URL 里先放个nlogin骗过匹配再放真实模块名就能不登录直接进任意后台模块/admin/index.php?nloginnbackupcdatabaseaindex当时用这个思路去访问 backup / user / config 等模块配合后台的注入点就能打。原代码截图源码protectedfunctioncheck_login(){global$_L;$_L[LCMSADMIN]SESSION::get(LCMSADMIN);$loginrootidSESSION::get(LCMSLOGINROOTID);if(stristr(HTTP_QUERY,nlogin)false!$_L[LCMSADMIN]){// ← 第 54 行拿 REQUEST_URI 子串判断能骗okinfo({$_L[url][admin]}index.php?rootid{$loginrootid}nlogingo.urlencode($_L[url][now]));}elseif($_L[LCMSADMIN]){修改后的代码改成只看真实加载的模块名L_NAMEfix_login_bypass.py重复参数这招就废了if(L_NAME!login!$_L[LCMSADMIN]){// ← 只认真实模块名okinfo({$_L[url][admin]}index.php?rootid{$loginrootid}nlogingo.urlencode($_L[url][now]));}elseif($_L[LCMSADMIN]){七、paycode(base64)/pay docheck SQL 注入漏洞类型SQL 注入base64 编码参数绕过 WAF 和关键字过滤形成原因这是被对手实打实利用过的点。前台pay入口的doindex()会base64_decode($_L[form][paycode])再json_decode然后丢给PAYS::pay()。妙就妙在base64 字符里不含任何 SQL 关键字所以sqlinsert的关键字过滤和 WAF 全都被绕过了解码后的order_no/payid再直接拼进 SQL。对手就是拿这个点配合 paycode 的cut/para这些 base64 参数打的SET GLOBAL general_log写马——当然后来确认他其实是直连 3306 干的这个注入点更多是被当成了怀疑对象但不管怎样它本身是实打实的洞。原代码截图源码publicstaticfunctionorder_info($order_no){$ordersql_get([order,order_no {$order_no}]);// ← 直接拼return$order;}publicstaticfunctionpayment_info($payid,$typefalse){global$_L;$paymentLCMS::form(array(tablepayment,doget,id$payid));// ← payid 没校验...}publicstaticfunctionorder($orderarray()){...sql_update([order,$order,order_no {$order[order_no]}]);// ← 也拼...}publicstaticfunctionpay($paycode,$openid){$paymentself::payment_info($paycode[payid],true);// ← payid 没 intval$payment?:LCMS::X(404,未找到支付参数);$order$paycode[order_no]?self::order([...]):LCMS::X(404,未找到订单信息);修改后的代码order_no白名单[A-Za-z0-9]、payid强制intvalfix_pays.pypublicstaticfunctionorder_info($order_no){$order_nopreg_match(/^[A-Za-z0-9]$/,$order_no)?$order_no:;$ordersql_get([order,order_no {$order_no}]);return$order;}publicstaticfunctionpayment_info($payid,$typefalse){global$_L;$payidintval($payid);$paymentLCMS::form(array(tablepayment,doget,id$payid));...}publicstaticfunctionorder($orderarray()){...$order[order_no]preg_match(/^[A-Za-z0-9]$/,$order[order_no])?$order[order_no]:;sql_update([order,$order,order_no {$order[order_no]}]);...}publicstaticfunctionpay($paycode,$openid){$paycode[payid]intval($paycode[payid]);$paycode[order_no]isset($paycode[order_no])preg_match(/^[A-Za-z0-9]$/,$paycode[order_no])?$paycode[order_no]:;$paymentself::payment_info($paycode[payid],true);...}八、支付回调 notify SQL 注入XML 绕过 WAF漏洞类型SQL 注入php://input/$_POST直达绕过 filterform 和 WAF形成原因4 个支付回调的notify.php都是独立入口不用登录$postObj直接取自php://inputXML或$_POST根本不走filterform过滤。这里最坑的是 wechat 的 XMLWAF 只查$_GET/$_POST/$_COOKIE不查php://input所以把注入 payload 塞进 XML 里就能绕过 WAF 直达 SQL。当时用union select sleep(3),2,...,12 --做过时间盲注验证lcms_order表 12 列是能通的报错注入因为 PDO SILENT 模式不回显只能走时间盲注。原代码截图源码// wechat / wechat_agent —— WxPay.Notify.phpXML 注入能绕过 WAF$postObjWxPayApi::FromXml(file_get_contents(php://input));if($postObj[out_trade_no]){$ordersql_get([order,order_no {$postObj[out_trade_no]}]);// ← 拼// alipay —— AliPay.Notify.php$postObj$_POST;if($postObj[out_trade_no]){$ordersql_get([order,order_no {$postObj[out_trade_no]}]);// ← 拼// codepay —— CodePay.Notify.php$postObj$_POST;if($postObj[pay_id]$postObj[pay_no]){$ordersql_get([order,order_no {$postObj[pay_id]}]);// ← 拼修改后的代码out_trade_no/pay_id全加白名单[A-Za-z0-9]fix_notify.py// wechat —— WxPay.Notify.php$postObjWxPayApi::FromXml(file_get_contents(php://input));$postObj[out_trade_no]isset($postObj[out_trade_no])?preg_replace(/^[A-Za-z0-9]$/,$0,$postObj[out_trade_no]):;$postObj[out_trade_no]preg_match(/^[A-Za-z0-9]$/,$postObj[out_trade_no])?$postObj[out_trade_no]:;if($postObj[out_trade_no]){$ordersql_get([order,order_no {$postObj[out_trade_no]}]);// wechat_agent —— WxPay.Notify.php$postObjWxPayApi::FromXml(file_get_contents(php://input));$postObj[out_trade_no]preg_match(/^[A-Za-z0-9]$/,$postObj[out_trade_no])?$postObj[out_trade_no]:;if($postObj[out_trade_no]){// alipay —— AliPay.Notify.php$postObj$_POST;$postObj[out_trade_no]preg_match(/^[A-Za-z0-9]$/,$postObj[out_trade_no])?$postObj[out_trade_no]:;if($postObj[out_trade_no]){// codepay —— CodePay.Notify.php$postObj$_POST;$postObj[pay_id]preg_match(/^[A-Za-z0-9]$/,$postObj[pay_id])?$postObj[pay_id]:;if($postObj[pay_id]$postObj[pay_no]){九、WAF 盲区不检查php://input漏洞类型防护缺失WAF 覆盖不到请求体原始流形成原因WAF v2 是我们自己部署在core/route.php里的只检查$_GET / $_POST / $_COOKIE不读php://input。结果就是第八节那个 wechat XML 注入能从 WAF 眼皮底下溜过去——WAF 查不到等于没查。源码?phperror_reporting(E_ERROR|E_PARSE|E_CORE_ERROR|E_COMPILE_ERROR|E_USER_ERROR);date_default_timezone_set(Asia/Shanghai);/* AWD WAF v2 START */if(!defined(AWDBUGKU_WAF)){define(AWDBUGKU_WAF,1);functionawd_waf_block($t){header(HTTP/1.1 403 Forbidden);header(Status: 403 Forbidden);exit(Request blocked);}functionawd_waf_check($val){if(is_array($val)){foreach($valas$v){awd_waf_check($v);}return;}if(!is_string($val)||$val){return;}if(preg_match(/(\beval\s*\(|\bassert\s*\(|\bsystem\s*\(|\bpassthru\s*\(|\bshell_exec\s*\(|\bpopen\s*\(|\bproc_open\s*\(|)/i,$val)){awd_waf_block(cmd);}if(preg_match(/(\bunion\b.\bselect\b|\bsleep\s*\(|\bbenchmark\s*\(|\bextractvalue\s*\(|\bupdatexml\s*\(|\bload_file\s*\(|\boutfile\b|information_schema)/i,$val)){awd_waf_block(sqli);}if(preg_match(/((\bor\b|\band\b)\s*[\\]\s*[\\]?\s*\d\s*\s*\d|(\bor\b|\band\b)\s*\d\s*\s*\d|--\s|\/\*)/i,$val)){awd_waf_block(booli);}if(preg_match(/(\/etc\/passwd|\/flag\b|\.\.\/|\.\.\\\\)/i,$val)){awd_waf_block(path);}if(preg_match(/\?(php|)/i,$val)){awd_waf_block(shell);}}awd_waf_check($_GET);awd_waf_check($_POST);awd_waf_check($_COOKIE);// ← 漏了 php://input}/* AWD WAF v2 END */修改后的代码WAF v3 补上php://input前 8KB 的检查fix_waf_v3.pyawd_waf_check($_GET);awd_waf_check($_POST);awd_waf_check($_COOKIE);$awd_rawfile_get_contents(php://input,false,null,0,8192);// ← 兜底读原始请求体if(is_string($awd_raw)$awd_raw!){awd_waf_check($awd_raw);}}/* AWD WAF v2 END */
返回列表