注入攻击是 web 领域最为常见的攻击方式, 其本质是把用户输入的数据当做代码执行, 主要原因是违背了数据与代码分离原则, 其发生的两个条件: 用户可以控制数据输入; 代码拼接了用户输入的数据, 把数据当做代码执行了.
下面是几种常见注入攻击及其防御方法:
SQL 注入及常见攻击技巧
经典注入 如:
- $username = $_POST['username'];
- $sql = "select * from usertable where username="."'".$username."'"
正常情况下用户输入'Tom' ,sql 为
"select * from usertable where username='Tom'";
但如果用户输入 "Tom' ; drop table usertable--",sql 如下:
"select * from usertable where username='Tom';drop table usertable--";
如果我们的服务端代码, 数据库权限没做任何处理, 那么结果伤不起.
并且当用户输入了可以导致拼接后 sql 有语法错的时候, 如果服务器开启了错误显示, 那么将会是攻击更加便利容易, 因为错误信息可能会暴露我们的数据库信息或者 sql 语句的雏形等等.
盲注:
大多数情况下服务器是关闭错误回显的, 没有提示的情况下, 攻击者又总结出了盲注: sql 注入过程中, sql 语句执行的选择后, 选择的数据不能回显到前端页面. 此时, 攻击者需要利用一些方法进行判断或者尝试, 这个过程称之为盲注.
攻击者通过真假命题的拼接根据页面返回情况判断 sql 漏洞是否存在.
比如: http://news.com/newsIndo.php?id=1, 执行的 sql 为 select * from newstable where id =1;
攻击者先是 http://news.com/newsIndo.php?id=1 and 1=2 然后 http://news.com/newsIndo.php?id=1 and 1=1
and 1=2 时 通常是空页面 或者错误信息, 如果 and 1=1 的时候页面正常返回, 就说明, and 语句成立, 也就说明 sql 漏洞存在, 攻击者不知道会拼接什么样的坏心思...
盲注高级技巧 Timing Attack 利用数据库自身函数的的执行造成的返回变化来判断漏洞是否存在, 比如 MySQL BENCHMARK() 函数.
数据库攻击机巧
常见 Payload
SQL 注入可以猜解出数据库的对应版本, 比如下面这段 Payload, 如果 MySQL 的版本是 4, 则会返回 true.
http://www.site.com/news.php?id=5 and substring(@@version,1,1)=4
下面这段 Payload, 则是利用 union select 来分别确认表名 admin 是否存在, 列名 passwd 是否存在:
- id=5 union all select 1,2,3 from admin
- id=5 union all select 1,2,passwd from admin
进一步, 想要猜解出 username 和 password 具体的值, 可以通过判断字符的范围, 一步步读出来.
id=5 and ascii(substring((select concat(username,0x3a,passwd) from users limit 0,1),1,1))>64
这个过程非常的繁琐, 所以非常有必要使用一个自动化工具来帮助完成整个过程.
sqlmap.py 就是一个非常好的自动化注入工具.
在注入攻击的过程中, 常常会用到一些读写文件的技巧. 比如在 MySQL 中, 就可以通过 LOAD_FILE() 读取系统文件, 并通过 INTO DUMPFILE 写入本地文件. 当然这要求当前数据库用户有读写系统相应文件或目录的权限.
命令执行
在 MySQL 中, 除了可以通过导出 webshell 间接地执行命令外, 还可以利用 "用户自定义函数" 的技巧, 即 UDF(user-defined functions) 来执行命令.
攻击存储过程
一些存储过程也是对攻击过程有帮助的, 比如: SQL Server 中的 xp_cmdshell, 注入时可以利用其执行系统命令. 存储过程本省也可能给存在注入漏洞.
编码问题
有时候不同的字符编码也可能导致注入漏洞的出现, 比如 转义符 '\' 可能会因为字符编码的不同被省略掉, 保证数据库, 操作系统, Web 应用的字符编码统一可以避免此类问题.
防御 SQL 注入
使用预编译语句
一般来说, 防御 sql 注入的最佳方式就是使用预编译语句, 绑定变量. 使用预编译的 sql 语句, 语句的语义不会发生改变. 在 sql 中变量用? 表示, 攻击者无法改变 sql 的结构, 比如下面 PHP 绑定变量, 使用预编译语句的示例:
- $sql = "insert into usertable (username,password,phone) value(?,?,?)";
- $stmt = $mysqli->prepare($sql);
- $stmt = bind_parm("sss",$username,$password,$phone);
- $username = 'Tom';
- $password = md5('123456');
- $phone = '13510345678';
- $stmt->execute();
此时就算攻击者插入类似于 Tom'or''='1 的字符串, 也只会将其当做 username 来插入, 不会改变语句的执行.
检查数据类型
对输入数据的数据类型进行检查, 可以很大程度上对抗 sql 注入, 比如整形 integer, 亦或是邮箱, 时间日期等特定格式数据.
使用安全函数
使用足够安全的编码函数, 比如: ESAPI.encoder().encodeForSQL( new OracleCodec(), queryparam );
- Codec ORACLE_CODEC = new OracleCodec();
- String query = "SELECT user_id FROM user_data WHERE user_name ='" +
- ESAPI.encoder().encodeForSQL( ORACLE_CODEC , req.getParameter("userID")) + "' and
- user_password = '"+ ESAPI.encoder().encodeForSQL( ORACLE_CODE , req.getParameter("pwd")) +"'";
最小权限及关闭错误回显
关闭服务器错误回显, 避免数据库信息的暴露, 数据库使用最小权限原则, 避免 root 等高级账号在 Web 应用中的直接使用, 数据库账号不应该有创建自定义函数, 操作本地文件等权限.
xml 注入
- $doc = new DOMDocument('1.0','utf-8');
- $doc -> formatOutput = true;
- $user = $doc -> createElement('user');
- $username = $doc -> createElement('username');
- $namevalue = $doc -> createTextNode($_POST['username']);
- $username -> appendChild($namevalue);// 将标签内容赋给标签
- $user -> appendChild($username);
- $doc -> appendChild($user);
- $doc -> save("user.xml");
正常情况下用户输入 Tom 将保存下面 xml 文件
- <?xml version="1.0" encoding="utf-8"?>
- <user>
- <username>Tom</username>
- </user>
但是如果用户输入恶意数据
Tom</username></user><user><username>Jack</username></user>
此时 xml 文件
- <?xml version="1.0" encoding="utf-8"?>
- <user>
- <username>Tom</username>
- </user>
- <user>
- <username>Jack</username>
- </user>
这就是 xml 注入. xml 注入, 可以对用户输入数据中的包含 "语言本身的保留字符" 行进转义即可.
代码注入
代码注入与命令注入都是由一些不安全函数或方法引起的如: eval()
- $var = 'varname';
- $k = $_GET['tag'];
- eval("/$var = $k;");
攻击者可以通过下面 Payload 实施代码注入:
/indexPHP?tag=1;phpinfo()
又比如 system() 这个函数, 可以通过代码注入导致命令注入
- $error =system('cat'.$_GET['page_id'],$return_var);
- echo $error;
攻击者通过 index.PHP?page_id=loquesea;ls 利用 system 函数执行自己想要的系统命令.
防御对抗代码注入, 命令注入, 需要禁用一些像 eval(),system() 等可以执行系统命令的函数, 非要用到就需要对用户输入的数据做处理, 此外 PHP 中动态 include 远程文件, 也是能避免就避免.
CRLF(\r\n) 注入
CRLF 其实是两个字符 \ n\r, 常常被用作语义之间的分隔符, 因此 CRLF 注入很可能改变原有的语义, 如下
- $log = fopen("login.log", "w");
- $txt = "user login failed for: \n";
- fwrite($log, $txt);
- fclose($log);
正常情况会记录如下在 login.log 日志文件中,
- user login failed for :user1
- user login failed for :user2
但是因为没有对换行 \n 做处理, 当攻击者输入 user2\nuser2 login succeeded for :user2
日志中结果就会变为
- user login failed for :user1
- user login failed for :user2
- user login succeeded for :user2
多了一条伪记录, user2 并没有登录成功.
CRLF 注入不仅仅可以 log 注入, 凡是利用 CRLF 作为分隔符的地方都可能存在这样的注入, 像是 "注入 HTTP 头" 等.
对抗 CRLF 注入其实只需要处理好 \r \n 这两个保留字符即可.
写在最后: 注入攻击主要是违背了数据与代码分离原则导致的, 其先决条件是用户能控制数据输入, 并且代码拼接了用户输入的数据, 把数据也打个代码执行了. 所以, 防御遵从数据域代码分离原则, 在拼接的地方做好安全检查和处理就能避免此类问题了.
来源: http://www.bubuko.com/infodetail-2824407.html