Linux Luminarium的道路
Hello Hackers
将教你与命令行交互的基础知识,命令行允许您执行命令。当启动终端时,它将执行命令行 “shell”,如下所示:
hacker@dojo:~$
1
输入:whoami
输入:hello
2
输入:echo Hello Hackers!
输入:hello hackers
Pondering Paths
绝对路径
- 定义:绝对路径是从根目录(“/”)开始,按照目录层级结构依次列出完整路径,直到指定的文件或目录。无论当前工作目录是什么,绝对路径都能准确无误地定位到目标。例如,/home/user/documents/file.txt 就是一个绝对路径,表示在根目录下的 home 目录中,user 子目录的 documents 子目录里有一个名为 file.txt 的文件。
- 特点:
唯一性和确定性:每个文件或目录都有唯一的绝对路径,不会因为当前工作目录的变化而改变。
便于跨目录操作:在不同的目录下,都可以使用绝对路径准确访问目标文件或目录,适合在脚本编写、系统配置文件中使用,以确保路径的准确性。 - 路径较长:当目录层级较深时,绝对路径可能会比较长,书写和记忆相对不便。
相对路径
- 定义:相对路径是相对于当前工作目录(Current Working Directory,CWD)来表示的路径。它不以根目录开头,而是通过与当前目录的相对位置关系来定位文件或目录。比如,当前工作目录是 /home/user,如果要访问 user 目录下 projects 子目录中的 main.py 文件,相对路径可以写成 projects/main.py。如果当前目录在 /home/user/documents,要访问上级目录中的 file.txt,可以使用 …/file.txt,这里的 … 表示上级目录。
- 特点:
依赖当前目录:相对路径的含义取决于当前工作目录,当当前目录改变时,相对路径所指向的目标也可能会发生变化。
简洁性:在同一目录层级或相近目录层级下操作时,相对路径通常更简洁,书写方便。 - 灵活性:在一些情况下,使用相对路径可以使代码或命令在不同的目录结构中具有更好的可移植性,只要目录之间的相对关系不变,相对路径就能正常工作。
1
输入:/pwn
2
输入:/challenge/run
3
可以先输入:/challenge/run 根据提示得出目标路径再cd
输入:cd /etc/apt/sources.list.d
输入:/challenge/run
4
输入:cd /proc/278
输入:/challenge/run
5
与3的同理
6
输入:cd /
输入:challenge/run
7
输入:cd /
输入:./challenge/run
8
当你输入一个 “直白” 的路径时,Linux 会明确避免自动在当前目录中查找。看下面这个例子:
hacker@dojo:~$ cd /challenge
hacker@dojo:/challenge$ run
这并不会调用 “/challenge/run” 程序。实际上,这是一种安全措施:如果每次你输入一个直白的路径时,Linux 都在当前目录中查找程序,那么你可能会意外地执行当前目录中那些碰巧与核心系统实用程序同名的程序!因此,上述命令会产生以下错误:
bash: run: bash: run: command not found
输入: cd /challenge
输入: ./run
9
每个用户都有一个主目录,在文件系统中通常位于 “/home” 目录下。,你是 “hacker” 用户,你的主目录是 “/home/hacker”。主目录通常是用户存储大部分个人文件的地方。一般来说,你的 shell 会话会以主目录作为当前工作目录启动。
这个提示符中的 “~” 代表当前工作目录,“~” 是 “/home/hacker” 的简写形式。bash 提供并使用这种简写,因为大多数时候你都在主目录中操作。因此,每当 bash 在与路径相关的参数中看到以 “~” 开头时,它会将其展开为你的主目录。
hacker@dojo:~$ cd /
hacker@dojo:/$ cd ~
hacker@dojo:~$ cd ~/asdf
hacker@dojo:~/asdf$ cd ~/asdf
hacker@dojo:~/asdf$ cd ~
hacker@dojo:~$ cd /home/hacker/asdf
hacker@dojo:~/asdf$
输入:/challenge/run ~/1
Comprehending Commands
1.cat:not a pet but the commend
cat 读出文件内容
cat 文件 (文件)(可以多个文件一起读)
输入:cat flag
2 cat
输入:cat /flag
3 cat
输入:cd (可以知道flag的位置)
输入:cat /opt/tcpdump/flag
4 grep文本搜索工具
grep 要查的东西 文件
输入:cat /challenge/data.txt
输入:grep pwn.college /challenge/data.txt
5 ls列出当前目录下的文件
输入:ls /challenge
输入:/challenge/1171-renamed-…(第一个文件)
6 touch 创建文件
输入:cd /tmp
输入:touch pwn
输入:touch college
输入:/challenge/run
7 rm 删除文件
输入:rm delete_me
输入:/challenge/check
8 ls 的
ls 命令在默认情况下并不会列出所有文件。Linux 有一个约定,即以点(.)开头的文件在 ls 命令以及其他一些情况下不会默认显示。要使用 ls 命令查看这些文件,你需要使用 -a 选项来调用 ls 命令
输入:cd /
输入:ls -a
输入:cat .flag-168521868820658
9
输入:ls -a
输入:cat SNIPPET
输入:cat opt/linux/linux-5.4/include/linux/netfilter_ipv4/REVELATION-TRAPPED
就循环
ls -a 目录 看文件名
cat 看文本
10 mkdir 创建目录
cd /
mkdir /tmp/pwn
cd /tmp/pwn
touch college
11 find 查找文件
find / -name flag
cd /
cat opt/linux/linux-5.4/Documentation/fb/flag
ln 链接 file 文件链接类型
cd /
ln -s /flag ~/not-the-flag
/challenge/catflag
Digesting Documentation 吃透文档
1 Learning
参数:像ls -a 中-a就是参数
/challenge/challenge --giveflag
2
/challenge/challenge --printfile /flag
3 man 命令手册
我们想了解 “yes” 命令(这是一个真实存在的命令):
hacker@dojo:~$ man yes
这将显示 “yes” 命令的手册页
你可以使用箭头键以及 “PgUp”/“PgDn” 键在手册页中滚动浏览。阅读完手册页后,按下 “q” 键即可退出。
手册页存储在一个集中式数据库中。这个数据库位于 /usr/share/man 目录下,但你无需直接与之交互:只需使用 “man” 命令进行查询即可。“man” 命令的参数不是文件路径,而只是条目本身的名称(例如,你运行 “man yes” 来查找 “yes” 命令的手册页,而不是 “man /usr/bin/yes”,/usr/bin/yes 是 “yes” 程序的实际路径,但这样做会导致 “man” 命令显示乱码)。
nan challenge
q
/challenge/challenge --fortune
/challenge/challenge --version
/challenge/challenge --774
4 / 手册中查找
使用 “/” 进行搜索
按 “n” 跳转到下一个搜索结果,按 “N” 跳转到上一个搜索结果。除了 “/”,你还可以使用 “?” 进行反向搜索!
man challenge
/flag 找到challenge的参数
/challenge/challenge --[参数]
5 查找手册
man man
man -k challenge 可以得到条目
man [条目]
/challenge/challenge --[]
6 特殊手册
有些程序没有手册页,但如果使用特殊参数调用,可能会告诉你如何运行它们。
通常,这个参数是–help,但也常常可能是-h,在极少数情况下,会是-?、help,或者像/?这样的特殊值(不过后者在 Windows 系统中更常见)。
/challenge/challenge --help
/challenge/challenge -p (得到一个数字)
/challenge/challenge -g 数字
7
有些命令并非是拥有手册页和帮助选项的程序,而是内置于 shell 自身当中。这些被称为内置命令。内置命令的调用方式与普通命令一样,但由 shell 在内部处理,而非启动其他程序。你可以通过运行 help 来获取 shell 内置命令的列表,如下所示:
hacker@dojo:~$ help
你可以将特定的内置命令作为参数传递给 help 内置命令,以获取其相关帮助。
hacker@dojo:~$ help cd
help challenge
challenge --secret cpO6vJ4Y
File Globbing
1 通配符*
自动补全,一个*可替换多个参数
第一个通配符是 * 当 shell 在任何参数中遇到 * 字符时,它会将其视为 “通配符”,并尝试用任何匹配该模式的文件替换该参数
它也可以很简单地只匹配一个.
hacker@dojo:~$ touch file_a
hacker@dojo:~$ touch file_b
hacker@dojo:~$ touch file_c
hacker@dojo:~$ ls
file_a file_b file_c
hacker@dojo:~$ echo Look: file_*
Look: file_a file_b file_c
当没有文件匹配时,默认情况下,shell 会保留通配符不变
hacker@dojo:~$ echo Look: nope_*
Look: nope_*
- 匹配文件名中除 / 或前导 . 字符之外的任何部分。例如:
hacker@dojo:~$ echo ONE: /ho*/*ck*
ONE: /home/hacker
hacker@dojo:~$ echo TWO: /*/hacker
TWO: /home/hacker
hacker@dojo:~$ echo THREE: ../*
THREE: ../hacker
cd /ch*
/challenge/run
2 ?
当 shell 在任何参数中遇到 ? 字符时,它会将其视为单字符通配符。它的作用与 * 类似,但一个?只能匹配一个字符。
hacker@dojo:~$ touch file_a
hacker@dojo:~$ touch file_b
hacker@dojo:~$ touch file_cc
hacker@dojo:~$ ls
file_a file_b file_cc
hacker@dojo:~$ echo Look: file_?
Look: file_a file_b
hacker@dojo:~$ echo Look: file_??
Look: file_cc
cd /?ha??enge
/challenge/run
3 []
方括号本质上是?的一种受限形式,它并非匹配任意字符,而是对括号内指定的部分可能字符进行通配。例如,[pwn] 将匹配字符 p、w 或 n。例如:
hacker@dojo:~$ touch file_a
hacker@dojo:~$ touch file_b
hacker@dojo:~$ touch file_c
hacker@dojo:~$ ls
file_a file_b file_c
hacker@dojo:~$ echo Look: file_[ab]
Look: file_a file_b
cd /challenge/files
/challenge/run file_[bash]
4 []
通配是基于路径进行的,所以你可以使用通配参数展开整个路径。
cd ~
/challenge/run /challenge/files/file_[bash]
5 通配符混合使用
通配符混合使用
在 Linux 系统里,通配符的混合使用能让你更灵活地匹配文件和目录。前面我们分别介绍了 *(匹配任意数量的任意字符,除了 / 和前导 .)、?(匹配单个任意字符)以及 [](匹配方括号内指定的任意一个字符) ,把它们组合起来能实现更复杂的匹配模式。
通配符代替的是开头的字符
* 和 [] 混合使用
若要匹配以 file_ 开头,后面接着 a 或 b 再跟着任意字符的文件,可以使用命令:
ls /challenge/files/file_[ab]*
这会匹配到 file_a、file_b、file_ab。
? 和 [] 混合使用
若要匹配以 file_ 开头,后面接着 1 或 2,再接着任意一个字符的文件,可以使用命令:
ls /challenge/files/file_[12]?
这会匹配到 file_1a、file_2a。
*、? 和 [] 一起混合使用
若要匹配以 file_ 开头,后面接着一个字母(a 到 c),再跟着任意数量字符的文件,可以使用命令:
ls /challenge/files/file_[a - c]*
这会匹配到 file_a、file_b、file_c、file_ab、file_ac。
cd /challenge/files
/challenge/run [cep]*
6 通配符过滤
你可能想在通配中过滤掉某些文件![] 可以帮你实现这一点。如果方括号中的第一个字符是 ! (或者在较新版本的 bash 中是 ^ ),通配规则会反转,即这个方括号实例会匹配未列出的字符。
hacker@dojo:~$ touch file_a
hacker@dojo:~$ touch file_b
hacker@dojo:~$ touch file_c
hacker@dojo:~$ ls
file_a file_b file_c
hacker@dojo:~$ echo Look: file_[!ab]
Look: file_c
hacker@dojo:~$ echo Look: file_[^ab]
Look: file_c
hacker@dojo:~$ echo Look: file_[ab]
Look: file_a file_b
cd /challenge/files
/challenge/run [^pwn]*
Practicing Piping 练习管道操作
你可能已经注意到,有些命令在运行时会将数据输出到终端上。
本模块将向你介绍输入和输出重定向。简单来说,Linux 中的每个进程最初都有三个标准通信通道:
标准输入:这是进程获取输入的通道。例如,你的 shell 会通过标准输入读取你输入的命令。
标准输出:进程通过这个通道输出正常数据,比如在之前的挑战中输出给你的标志(flag),或者像 ls 这样的工具的输出结果。
标准错误:进程通过这个通道输出错误详情。例如,如果你输错了一个命令,shell 会通过标准错误输出提示该命令不存在。
由于这三个通道在 Linux 中使用极为频繁,所以它们有更简短的名称:标准输入(stdin)、标准输出(stdout)、标准错误(stderr)。本模块将教你如何对这些通道进行重定向、链接、阻塞以及其他相关操作。
1 标准重定向输出
首先,我们来看看如何将标准输出重定向到文件。你可以使用 “>” 字符来实现这一点,如下所示:
hacker@dojo:~$ echo hi > asdf
这会将 “echo hi” 的输出(也就是 “hi”)重定向到文件 “asdf” 中。然后,你可以使用诸如 “cat” 这样的程序来输出这个文件的内容:
hacker@dojo:~$ cat asdf
hi
echo PWN > CPLLEGE
2 其他重定向输出
除了重定向echo命令的输出,当然你也可以重定向任何命令的输出。
你会注意到,尽管你重定向了标准输出,/challenge/run 仍会在你的终端上正常打印信息。这是因为它通过标准错误来传达指令和反馈,而仅通过标准输出打印标志!
/challenge/run > myflag
cat myflag
3 重定向输出的用例
输出重定向的一个常见用例是保存某些命令的结果,以便日后分析。会想以汇总的方式来做这件事:运行一系列命令,保存它们的输出,之后再通过 grep 命令在其中查找。在这种情况下,你可能希望所有输出都持续追加到同一个文件中,但 “>” 每次都会创建一个新的输出文件,删除旧的内容。你可以使用 “>>” 而不是 “>” 以追加模式重定向输入
hacker@dojo:~$ echo pwn > outfile
hacker@dojo:~$ echo college >> outfile
hacker@dojo:~$ cat outfile
pwn
college
hacker@dojo:$
/challenge/run >> /home/hacker/the-flag
cat /home/hacker/the-flag
4 重定向错误
和标准输出一样,你也可以重定向命令的错误通道。在这里,我们将了解文件描述符编号。文件描述符(FD)是一个用于描述 Linux 中通信通道的数字。即便你没意识到,其实你已经在使用它们了。我们已经熟悉以下三个文件描述符:
FD 0: Standard Input
FD 1: Standard Output
FD 2: Standard Error
当你重定向进程通信时,是通过文件描述符编号来进行的,不过有些文件描述符编号是隐式的。例如,没有数字的 “>” 意味着 “1>”,即重定向文件描述符 1(标准输出)。因此,以下两条命令是等价的:
hacker@dojo:~$ echo hi > asdf
hacker@dojo:~$ echo hi 1> asdf
如果你有一个可能会通过标准错误输出数据的命令(例如 /challenge/run),你可以这样做:
hacker@dojo:~$ /challenge/run 2> errors.log
这会将标准错误(FD 2)重定向到 errors.log 文件。
你还可以同时重定向多个文件描述符!
hacker@dojo:~$ some_command > output.log 2> errors.log
该命令会将输出重定向到 output.log 文件,将错误重定向到 errors.log 文件。
/challenge/run > myflag 2> instructions
cat myflag
5 重定向输入
用< 重定向输入
hacker@dojo:~$ echo yo > message
hacker@dojo:~$ cat message
yo
hacker@dojo:~$ rev < message
oy
echo COLLEGE > PWN
/challenge/run < PWN
6 grep查找
/challenge/run > /tmp/data.txt
grep pwn /tmp/data.txt
grep语法回顾
grep 要查的东西 文件
7 | 管道传输
你可以 “跳过中间人”,避免像在上一关那样将结果存储到文件中。你可以通过使用 |(管道)运算符来实现这一点。管道左侧命令的标准输出将被连接到(传输到)管道右侧命令的标准输入。
hacker@dojo:~$ echo no-no | grep yes
hacker@dojo:~$ echo yes-yes | grep yes
yes-yes
hacker@dojo:~$ echo yes-yes | grep no
hacker@dojo:~$ echo no-no | grep no
no-no
/challenge/run | grep pwn
8 >& 文件描述符的重定向
如果你想直接在错误信息中用grep搜索
shell 有一个>&操作符,它将一个文件描述符重定向到另一个文件描述符。这意味着我们可以分两步在错误信息中使用grep:首先,我们将标准错误重定向到标准输出(2>&1),然后像平常一样将合并后的标准错误和标准输出通过管道传输(|)
/challenge/run 2>&1 /challenge/.data.txt | grep pwn
9 tee 管道传输显示
当你将数据从一个命令通过管道传输到另一个命令时,你当然就无法再在屏幕上看到这些数据了。你可能想要看到数据在命令之间传输时的样子,以便调试意外结果。
tee命令得名于管道中的 “T 型分流器”,它会将通过管道传输的数据复制到命令行中指定的任意数量的文件里。(相当副本,或者中间加一个管道)
hacker@dojo:~$ echo hi | tee pwn college
hi
hacker@dojo:~$ cat pwn
hi
hacker@dojo:~$ cat college
hi
hacker@dojo:~$
这个真磕了好长时间
/challenge/pwn | /challegne/college
/challenge/pwn | tee problem | /challenge/college
cat problem
/challenge/pwn --secret 密钥 | /challenge/college
10 tee知识扩展
可以使用tee将数据复制到两个文件中
hacker@dojo:~$ echo HACK | tee THE > PLANET
hacker@dojo:~$ cat THE
HACK
hacker@dojo:~$ cat PLANET
HACK
hacker@dojo:~$
你已经用过tee将数据复制到一个文件和一个命令中
hacker@dojo:~$ echo HACK | tee THE | cat
HACK
hacker@dojo:~$ cat THE
HACK
hacker@dojo:~$
但要是将数据复制到两个命令中呢
tee 是从标准输入读取数据,并写入标准输出和文件
命令与文件 >()
Linux 遵循 “一切皆文件” 的理念。也就是说,系统努力为大多数资源提供类似文件的访问方式,包括正在运行的程序的输入和输出!
shell 遵循这一理念,例如,允许你在命令行中使用任何接受文件参数的工具(如tee),并将其连接到程序的输入或输出端!
这是通过所谓的进程替换来实现的。如果你写一个>(rev)这样的参数,bash 会运行rev命令(这个命令从标准输入读取数据,颠倒其顺序,然后写入标准输出!),但会将其输入连接到它将创建的一个临时文件。当然,这不是一个真正的文件,它是所谓的命名管道,因为它有一个文件名:
hacker@dojo:~$ echo >(rev)
/dev/fd/63
hacker@dojo:~$
/dev/fd/63是从哪里来的呢?bash 用连接到rev输入的命名管道文件的路径替换了>(rev)!在命令运行时,写入这个文件会将数据通过管道传输到该命令的标准输入。通常,这是使用接受输出文件作为参数的命令(如tee)来完成的:
hacker@dojo:~$ echo HACK | rev
KCAH
hacker@dojo:~$ echo HACK | tee >(rev)
HACK
KCAH
上述过程中发生了以下一系列事件:
- bash 启动rev命令,将一个命名管道(大概 是/dev/fd/63)连接到rev的标准输入。
- bash 启动tee命令,将一个管道连接到其标准输入,并将tee的第一个参数替换为/dev/fd/63。tee甚至从未看到>(rev)这个参数;在启动tee之前,shell 就已经对其进行了替换。
- bash 使用内置的echo命令将HACK输出到tee的标准输入。
- tee读取HACK,将其写入标准输出,然后写入/dev/fd/63(它连接到rev的标准输入)。
- rev从其标准输入读取HACK,将其颠倒顺序,并将KCAH写入标准输出。
以下操作是等效的:
hacker@dojo:~$ echo hi | rev
ih
hacker@dojo:~$ echo hi > >(rev)
ih
hacker@dojo:~$
hacker@dojo:~$ echo hi | rev | rev
hi
hacker@dojo:~$ echo hi > >(rev | rev)
hi
hacker@dojo:~$
/challenge/hack | tee >(/challenge/the) | tee >(/challenge/planet)
11 总结练习,标准输出与标准错误输出同时使用
/challenge/hack > >(/challenge/planet) 2> >(/challenge/the)
shell variables shell变量
Linux 命令行界面实际上是一种复杂的编程语言,你可以用它编写实实在在的程序!
由于命令行界面通俗来讲被称为 “shell”,用这种语言编写的程序就被称作 “shell 脚本”。当你使用命令行时,基本上就是在逐行编写一个 shell 脚本!
和大多数编程语言一样,shell 支持变量。
1 打印变量
你可以用 echo 输出变量,只需在变量名前加上 $ 符号。例如,有一个变量 PWD,它始终保存着当前 shell 的工作目录。你可以这样输出它:
hacker@dojo:~$ echo $PWD
/home/hacker
echo $FLAG
2 设立变量
你还可以给变量写入值。这通过 “=” 来完成。要将变量 VAR 设置为值 1337,你可以使用:
hacker@dojo:~$ VAR=1337
**注意,“=” 周围没有空格!**如果你加上空格(例如,VAR = 1337),shell 将无法识别这是变量赋值操作,而是会尝试运行 VAR 命令(而该命令并不存在)。
还要注意,这里使用的是 VAR 而不是$var
PWN=COLLEGE
3 多单词赋值给变量 引号的作用
如果你想把它设置为 “1337 SAUCE” 呢?你可能会尝试以下操作:
hacker@dojo:~$ VAR=1337 SAUCE
这看起来合理,但行不通,原因与 “=” 周围不能有空格类似。当 shell 看到空格时,它会结束变量赋值,并将下一个单词(在这个例子中是 SAUCE)解释为一条命令。
要将 VAR 设置为 “1337 SAUCE”,你需要加上引号:
hacker@dojo:~$ VAR="1337 SAUCE"
PWN=“COLLEGE YEAH”
4 可继承变量
你在 shell 会话中设置的变量仅在该 shell 进程内有效。也就是说,你运行的其他命令不会继承这些变量。
你可以通过在当前 shell 中调用另一个 shell 进程来验证这一点,如下所示:
hacker@dojo:~$ VAR=1337
hacker@dojo:~$ echo "VAR is: $VAR"
VAR is: 1337
hacker@dojo:~$ sh
$ echo "VAR is: $VAR"
VAR is:
在上述输出中,“$” 提示符是 sh 的提示符,sh 是一个精简的 shell 实现,作为主 shell 进程的子进程被调用。它并没有接收到 VAR 变量!
这当然是合理的。你的 shell 变量可能包含敏感或特殊的数据,除非明确需要,否则你不希望这些数据泄露到你运行的其他程序中。
那么如何表明数据应该被传递呢?你需要导出变量。当你导出变量时,它们会被传递到子进程的环境变量中。
hacker@dojo:~$ VAR=1337
hacker@dojo:~$ export VAR
hacker@dojo:~$ sh
$ echo "VAR is: $VAR"
VAR is: 1337
子 shell 接收到了 VAR 的值并能够将其打印出来!你也可以把前两行合并。
hacker@dojo:~$ export VAR=1337
hacker@dojo:~$ sh
$ echo "VAR is: $VAR"
VAR is: 1337
export PWN=COLLEGE
/challenge/run
COLLEGE=PWN
/challenge/run
5 打印全局变量 env
试试 env 命令:它会打印出你在 shell 中设置的每一个导出变量,你可以在输出中查找 FLAG 变量!
env 打印所有设置的变量
env
之后就找吧,一找一个不吱声
6 存储命令输出 $()
你会希望将某个命令的输出存储到一个变量中。借助一种称为命令替换的功能,看示例:
hacker@dojo:~$ FLAG=$(cat /flag)
hacker@dojo:~$ echo "$FLAG"
pwn.college{blahblahblah}
hacker@dojo:~$
PWN=$(/challenge/run)
echo “$PWN”
7 读取输入 read
我们将从读取用户(也就是你)的输入开始。这可以通过恰如其名的内置命令 read 来完成,它用于读取输入!
read 从你的标准输入读取数据!上面的第一个 “Hello!” 是输入而非输出。
hacker@dojo:~$ read -p "INPUT: " MY_VARIABLE
INPUT: Hello!
hacker@dojo:~$ echo "You entered: $MY_VARIABLE"
You entered: Hello!
注意hello是你自己输的
感觉和scanf相似
我们在每行开头标注该行代表用户的输入(INPUT)还是输出给用户的内容(OUTPUT)
INPUT: hacker@dojo:~$ echo $MY_VARIABLE
OUTPUT:
INPUT: hacker@dojo:~$ read MY_VARIABLE
INPUT: Hello!
INPUT: hacker@dojo:~$ echo "You entered: $MY_VARIABLE"
OUTPUT: You entered: Hello!
read PWN
COLLEGE
8 读取文件
通常,当 shell 用户想要将文件内容读取到环境变量中时,他们会这样做:
hacker@dojo:~$ echo "test" > some_file
hacker@dojo:~$ VAR=$(cat some_file)
hacker@dojo:~$ echo $VAR
test
你学习了将用户输入读取到变量中。你也学过将文件重定向到命令的输入!把这两者结合起来,就可以用 shell 读取文件了。
hacker@dojo:~$ echo "test" > some_file
hacker@dojo:~$ read VAR < some_file
hacker@dojo:~$ echo $VAR
test
该示例将 some_file 重定向到 read 命令的标准输入,所以当 read 读取内容到 VAR 时,它读取的是文件中的内容
read “PWN” < /challenge/read_me
Processes and Jobs 进程与作业
计算机通过执行软件来完成任务。在现代计算中,软件分为两类:操作系统内核和进程。
Linux 启动时,会启动一个 init(初始化程序的缩写)进程,该进程又会启动一系列其他进程,这些进程再启动更多进程,最终,你看到的命令行 shell 也是一个进程!当然,shell 会根据你输入的命令来启动进程。
1 列出进程 ps
ps命令列出正在运行的进程,默认情况下,ps只列出在你终端中运行的进程,说实话,这用处不大
hacker@dojo:~$ ps
PID TTY TIME CMD
329 pts/0 00:00:00 bash
349 pts/0 00:00:00 ps
hacker@dojo:~$
我们看到了 shell(bash)和ps进程本身,这就是在那个特定终端上运行的所有进程。
我们还看到每个进程都有一个数字标识符(进程 ID,即 PID),这是一个在 Linux 环境中唯一标识每个正在运行进程的数字。
我们也看到了命令运行所在的终端(在这个例子中是pts/0),以及该进程到目前为止消耗的 CPU 总时间(由于这些进程对资源需求很低,它们甚至还没消耗 1 秒钟的时间!)。
使用默认的ps你只能看到这些。为了让它更有用,我们需要传递一些参数.有两种指定参数的方式。
- 标准” 语法:在这种语法中,你可以使用-e列出 “所有” 进程,使用-f以 “完整格式” 输出,包括参数。这些可以组合成一个参数-ef。
- “BSD” 语法:在这种语法中,你可以使用a列出所有用户的进程,x列出不在终端中运行的进程,u以 “用户可读” 格式输出。这些可以组合成一个参数aux。
ps -ef和ps aux这两种方法产生的输出略有不同,但相互之间是可识别的。
hacker@dojo:~$ ps -ef
UID PID PPID C STIME TTY TIME CMD
hacker 1 0 0 05:34? 00:00:00 /sbin/docker-init -- /bin/sleep 6h
hacker 7 1 0 05:34? 00:00:00 /bin/sleep 6h
hacker 102 1 1 05:34? 00:00:00 /usr/lib/code-server/lib/node /usr/lib/code-server --auth=none -
hacker 138 102 11 05:34? 00:00:07 /usr/lib/code-server/lib/node /usr/lib/code-server/out/node/entr
hacker 287 138 0 05:34? 00:00:00 /usr/lib/code-server/lib/node /usr/lib/code-server/lib/vscode/ou
hacker 318 138 6 05:34? 00:00:03 /usr/lib/code-server/lib/node --dns-result-order=ipv4first /usr/
hacker 554 138 3 05:35? 00:00:00 /usr/lib/code-server/lib/node /usr/lib/code-server/lib/vscode/ou
hacker 571 554 0 05:35 pts/0 00:00:00 /usr/bin/bash --init-file /usr/lib/code-server/lib/vscode/out/vs
hacker 695 571 0 05:35 pts/0 00:00:00 ps -ef
hacker@dojo:~$
有用于初始化挑战环境的进程(docker - init)、为节省计算资源在挑战自动终止前设置的超时进程(sleep 6h表示 6 小时后超时)、VSCode 环境相关的进程(几个code - server辅助进程)、shell(bash),以及我执行的ps -ef命令。
hacker@dojo:~$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
hacker 1 0.0 0.0 1128 4? Ss 05:34 0:00 /sbin/docker-init -- /bin/sleep 6h
hacker 7 0.0 0.0 2736 580? S 05:34 0:00 /bin/sleep 6h
hacker 102 0.4 0.0 723944 64660? Sl 05:34 0:00 /usr/lib/code-server/lib/node /usr/lib/code-serve
hacker 138 3.3 0.0 968792 106272? Sl 05:34 0:07 /usr/lib/code-server/lib/node /usr/lib/code-serve
hacker 287 0.0 0.0 717648 53136? Sl 05:34 0:00 /usr/lib/code-server/lib/node /usr/lib/code-serve
hacker 318 3.3 0.0 977472 98256? Sl 05:34 0:06 /usr/lib/code-server/lib/node --dns-result-order=
hacker 554 0.4 0.0 650560 55360? Rl 05:35 0:00 /usr/lib/code-server/lib/node /usr/lib/code-serve
hacker 571 0.0 0.0 4600 4032 pts/0 Ss 05:35 0:00 /usr/bin/bash --init-file /usr/lib/code-server/li
hacker 1172 0.0 0.0 5892 2924 pts/0 R+ 05:38 0:00 ps aux
hacker@dojo:~$
ps -ef和ps aux有很多共同之处:两者都显示用户(USER列)、PID、TTY、进程启动时间(STIME/START)、总 CPU 使用时间(TIME)以及命令(CMD/COMMAND)。
ps -ef还会输出父进程 ID(PPID),即启动该进程的进程的 PID,而ps aux会输出该进程占用系统 CPU 和内存的百分比。
ps -ef
/challenge/你在进程中查到的
2 结束进程 kill
kill命令会以一种让进程有机会在终止前清理好自身事务的方式来结束它。
你可以使用kill命令,将进程标识符(即ps命令显示的 PID)作为参数传入来终止它
hacker@dojo:~$ ps -e | grep sleep
342 pts/0 00:00:00 sleep
hacker@dojo:~$ kill 342
hacker@dojo:~$ ps -e | grep sleep
hacker@dojo:~$
ps -ef
kill 120
/challenge/run
3 中断进程 ctrl+c
终端为此设置了一个快捷键:Ctrl - C(即按住 Ctrl 键并按下 C 键)会向任何正在等待终端输入的应用程序发送一个 “中断” 信号,通常情况下,这会使应用程序正常退出。
/challenge/run
ctrl + c
4 ctrl + z 挂在后端
ctrl + z 将进程挂在后端
/challenge/run
ctrl+z
/challenge/run
5 fg 回复挂起的进程到前台
当你挂起进程时,你会希望在某个时候恢复它们。
为了恢复进程,你的 shell 提供了 fg 命令,这是一个内置命令,它会获取被挂起的进程,恢复该进程,并将其放回终端前台。
fg命令将进程恢复到前台运行
/challenge/run
ctrl + z
fg
6 bg 恢复到后台
你还可以使用bg命令将进程恢复到后台运行!这样一来,进程能继续运行,同时你又可以使用 shell 来执行更多命令。
用终端启动它,然后挂起,接着用bg命令将其放到后台运行,同时在第一个副本后台运行时启动另一个副本!
解一下如何查看挂起和后台运行属性之间的差异
hacker@dojo:~$ sleep 1337
^Z
[1]+ Stopped sleep 1337
hacker@dojo:~$
sleep进程已在后台挂起。我们可以使用ps命令,并通过-o选项启用stat列输出,来查看这一情况:
hacker@dojo:~$ ps -o user,pid,stat,cmd
USER PID STAT CMD
hacker 702 Ss bash
hacker 762 T sleep 1337
hacker 782 R+ ps -o user,pid,stat,cmd
hacker@dojo:~$
看到那个T了吗?这意味着进程因为我们按下Ctrl-Z而被挂起。bash的STAT列中的S表示bash在等待输入时处于睡眠状态。ps列中的R表示它正在积极运行,而+表示它处于前台!
/challenge/run
ctrl + z
bg
/challenge/run
7将进程放前台 fg
你有一个在后台运行的进程,并且你还想对它做更多操作。你会怎么做呢?嗯,你可以像将挂起的进程调到前台一样,使用 fg 命令将后台运行的进程调到前台。
/challenge/run
ctrl + z
bg
fg
8 直接在后台启动 &
你不一定要先挂起进程再将其置于后台运行:你可以直接让进程在后台启动!这很简单,你只需在命令后面加上 & 即可
/challenge/run &
9 进程退出代码 $?
每个 shell 命令,包括每个程序和每个内置命令,在运行结束并终止时都会返回一个退出代码。shell 或者 shell 的用户(也就是你)可以使用这个退出代码来检查进程是否按预期完成功能(当然,这个判断取决于进程原本应该执行的任务)。
你可以使用特殊变量 ? 来获取最近终止的命令的退出代码(别忘了在前面加上 $ 来读取它的值!)
hacker@dojo:~$ touch test-file
hacker@dojo:~$ echo $?
0
hacker@dojo:~$ touch /test-file
touch: cannot touch '/test-file': Permission denied
hacker@dojo:~$ echo $?
1
hacker@dojo:~$
如你所见,成功的命令通常返回 0,失败的命令通常返回非零值,最常见的是 1,但有时也会返回一个错误代码,用于识别特定的失败模式。
/challenge/get-code
echo $?
/challenge/submit-code 207
Perceiving Permissions感知权限
在 Linux 中,文件具有不同的权限或文件模式。你可以使用ls -l查看文件或目录的权限。
hacker@dojo:~$ mkdir pwn_directory
hacker@dojo:~$ touch college_file
hacker@dojo:~$ ls -l
total 4
-rw-r--r-- 1 hacker hacker 0 May 22 13:42 college_file
drwxr-xr-x 2 hacker hacker 4096 May 22 13:42 pwn_directory
hacker@dojo:~$
文件类型
每行的第一个字符代表文件类型。对于pwn_directory
,字母d
表示它是一个目录;对于college_file
,短横线-
表示它是一个普通文件。还有其他文件类型
权限
接下来的九个字符是文件或目录的实际访问权限,分为三组,每组三个字符。
第一组三个字符表示拥有该文件的用户(称为“所有者”)对文件的权限,第二组三个字符表示拥有该文件的组(称为“组”)对文件的权限,第三组三个字符表示所有其他访问者(例如其他用户和其他组)对文件的权限。在本模块后面我们将详细学习这些内容。
所有权信息
有两列,分别显示拥有该文件的用户(在这个例子中是用户“hacker”),以及拥有该文件的组(在这个例子中也是组“hacker”)。你将在这里对其进行操作!
1 改变文件的所属权 chown
首先要讲的是:文件所有权。在 Linux 系统中,每个文件都归系统中的某个用户所有。在日常使用中,这个用户通常就是你每天登录系统所用的用户。
在共享系统(比如计算机实验室的系统)中,可能会有许多人拥有不同的用户账户,他们在各自的主目录中都有自己的文件。但即使在非共享系统(比如你的个人电脑)中,Linux 也有许多用于不同任务的 “服务” 用户账户。
最重要的两个用户账户是:
- 你的用户账户!
- root。这是管理员账户,在大多数安全场景中,它是终极目标。如果你获取了 root 用户权限,那几乎可以肯定你已经达成了黑客攻击目标!
hacker@dojo:~$ ls -l /flag
-r-------- 1 root root 53 Jul 4 04:47 /flag
hacker@dojo:~$ cat /flag
cat: /flag: Permission denied
hacker@dojo:~$
在这里,你可以看到 flag 文件归 root 用户(这一行中的第一个 “root”)和 root 组(这一行中的第二个 “root”)所有。当我们尝试以 hacker 用户身份读取它时,会被拒绝。然而,如果我们是 root 用户,读取这个文件就毫无问题:
root@dojo:~# cat /flag
pwn.college{demo_flag}
root@dojo:~#
我们可以更改文件的所有权!这可以通过 chown(change owner,更改所有者)命令来完成:
chown [用户名] [文件]
own 命令只能由 root 用户调用。我们再假装自己是 root 用户,看看 chown 的典型用法
root@dojo:~# mkdir pwn_directory
root@dojo:~# touch college_file
root@dojo:~# ls -l
total 4
-rw-r--r-- 1 root root 0 May 22 13:42 college_file
drwxr-xr-x 2 root root 4096 May 22 13:42 pwn_directory
root@dojo:~# chown hacker college_file
root@dojo:~# ls -l
total 4
-rw-r--r-- 1 hacker root 0 May 22 13:42 college_file
drwxr-xr-x 2 root root 4096 May 22 13:42 pwn_directory
root@dojo:~#
chown hacker /flag
cat /flag
2 组和文件 (id)(ls -l 文件)(chgrp)
文件既有所属用户,也有所属组。一个组可以包含多个用户,一个用户也可以是多个组的成员。
你可以使用id命令查看自己属于哪些组:
hacker@dojo:~$ id
uid=1000(hacker) gid=1000(hacker) groups=1000(hacker)
hacker@dojo:~$
组最常见的用途是控制对不同系统资源的访问。
典型的 Linux 桌面系统的普通主用户会属于很多组。例如,这是 Zardus 的桌面系统情况
zardus@yourcomputer:~$ id
uid=1000(zardus) gid=1000(zardus) groups=1000(zardus),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),114(bluetooth),117(lpadmin),120(scanner),995(docker)
zardus@yourcomputer:~$
所有这些组赋予了 Zardus 读取 CD 和软盘的能力(现在还有谁这么做呢?)、管理系统、播放音乐、向视频显示器输出内容、使用蓝牙等等。通常,这种访问控制是通过文件系统上的组所有权来实现的!例如,图形输出可以通过特殊的/dev/fb0文件完成:
zardus@yourcomputer:~$ ls -l /dev/fb0
crw-rw---- 1 root video 29, 0 Jun 30 23:42 /dev/fb0
zardus@yourcomputer:~$
这个文件是一个特殊的设备文件(类型c表示它是一个 “字符设备”),与它交互会导致显示输出的变化(而不像普通文件那样改变磁盘存储内容!)。Zardus 在他机器上的用户账户可以与它交互,因为该文件的组所有权是video,而 Zardus 是video组的成员。
在这个组里就可以访问同一组里的文件
flag文件归root用户和root组所有,而hacker用户既不是root用户,也不是root组的成员,所以无法访问该文件。
可以使用chgrp(更改组change group)命令来更改组所有权!除非你对该文件有写入权限并且是新组的成员,否则这通常需要 root 权限
chgrp 组名 文件
root@dojo:~# mkdir pwn_directory
root@dojo:~# touch college_file
root@dojo:~# ls -l
total 4
-rw-r--r-- 1 root root 0 May 22 13:42 college_file
drwxr-xr-x 2 root root 4096 May 22 13:42 pwn_directory
root@dojo:~# chgrp hacker college_file
root@dojo:~# ls -l
total 4
-rw-r--r-- 1 root hacker 0 May 22 13:42 college_file
drwxr-xr-x 2 root root 4096 May 22 13:42 pwn_directory
root@dojo:~#
chgrp hacker /flag
cat /flag
3 组名 id
你的 “hacker” 用户是 “hacker” 组的成员,而 “zardus” 是 “zardus” 组的成员。在 Linux 中有这样一个惯例,即每个用户都有自己的组,但并非一定如此。例如,许多计算机实验室会将所有用户归入一个共享的 “users” 组。
id
chgrep grp8374 /flag
cat /flag
4 改变权限 chmod
hacker@dojo:~$ mkdir pwn_directory
hacker@dojo:~$ touch college_file
hacker@dojo:~$ ls -l
total 4
-rw-r--r-- 1 hacker hacker 0 May 22 13:42 college_file
drwxr-xr-x 2 hacker hacker 4096 May 22 13:42 pwn_directory
hacker@dojo:~$
这里的第一个字符表示文件类型。接下来的九个字符是文件或目录的实际访问权限,分为三组,每组三个字符,分别表示文件所有者、所属组以及其他所有访问者(例如其他用户和其他组)对文件的权限。
这三组中的每个字符代表不同类型的权限:
r
- 用户 / 组 / 其他用户可以读取文件(或列出目录内容)w
- 用户 / 组 / 其他用户可以修改文件(或在目录中创建 / 删除文件)x
- 用户 / 组 / 其他用户可以将文件作为程序执行(或者可以进入目录,例如使用cd命令)-
- 无权限
对于上面的college_file文件,rw-r–r–权限解析如下:
r:文件所有者(hacker用户)可以读取它
w:文件所有者(hacker用户)可以写入它
-:文件所有者(hacker用户)不能执行它
r:文件所属组(hacker组)中的用户可以读取它
-:文件所属组(hacker组)中的用户不能写入它
-:文件所属组(hacker组)中的用户不能执行它
r:其他所有用户可以读取它
-:其他所有用户不能写入它
-:其他所有用户不能执行它
和所有权一样,文件权限也可以更改。这可以通过chmod(更改模式change mod)命令来完成。chmod的基本用法是:chmod [选项] 模式 文件
你可以通过两种方式指定 “模式”:作为对现有权限模式的修改,或者作为一个全新的模式来覆盖旧模式。
修改现有模式。chmod允许你使用WHO+/-WHAT的模式格式来调整权限,其中WHO代表用户(user)/ 组(group)/ 其他用户(other),WHAT代表读(read)/ 写(write)/ 执行(execute)。例如,要为文件所有者添加读取权限,你可以指定u+r模式。为组和其他用户(或所有权限)添加写和执行权限的指定方式相同。更多示例:
u+r,如上述所说,为用户权限添加读取权限
g+wx为组权限添加写和执行权限
o-w移除其他用户的写权限
a-rwx移除用户、组和其他所有人的所有权限
root@dojo:~# mkdir pwn_directory
root@dojo:~# touch college_file
root@dojo:~# ls -l
total 4
-rw-r--r-- 1 root root 0 May 22 13:42 college_file
drwxr-xr-x 2 root root 4096 May 22 13:42 pwn_directory
root@dojo:~# chmod go-rwx *
root@dojo:~# ls -l
total 4
-rw------- 1 hacker root 0 May 22 13:42 college_file
drwx------ 2 root root 4096 May 22 13:42 pwn_directory
root@dojo:~#
通常情况下,你需要对文件有写权限才能更改其权限
chmod ugo+rwx /flag
5 可执行文件
只有在你对该程序文件拥有执行访问权限的情况下,Linux 才会真正执行它。
ls -l /challenge/run(文件名) 看他的权限
hacker@dojo:~$ ls -l /challenge/run
-rwxr-xr-x 1 root root 0 May 22 13:42 /challenge/run
hacker@dojo:~$ /challenge/run
Successfully ran the challenge!
hacker@dojo:~$
chmod u+rwx /challenge/run
/challenge/run
6 更改权限
chmod g-r /challenge/pwn
chmod g+rw /challenge/pwn
chmod o+w /challenge/pwn
chmod ug-r /challenge/pwn
chmod o+x /challenge/pwn
chmod g+r /challenge/pwn
chmod o-r /challenge/pwn
chmod ug-rw /challenge/pwn
chmod ugo+r /flag
cat /flag
7 设立权限
除了添加和移除权限,chmod 还能直接整体设置权限,覆盖旧权限。这通过使用 = 而不是 - 或 + 来实现。例如:
u=rw
为用户设置读写权限,并清除执行权限。o=x
只为其他用户设置执行权限,清除读写权限。a=rwx
为用户、组和其他所有人设置读、写和执行权限!
你可以通过在 chmod 中用 , 连接多个权限模式来实现文件所有者用户设置 rw 权限,但只为所属组设置 r 权限
chmod u=rw,g=r /challenge/pwn
会将用户权限设置为读写,将组权限设置为只读。chmod a=r,u=rw /challenge/pwn
会将用户权限设置为读写,将组和其他所有人的权限设置为只读。
你可以用 - 来清除权限:
chmod u=rw,g=r,o=- /challenge/pwn
会将用户权限设置为读写,将组权限设置为只读,将其他所有人的权限设置为无任何权限。
出现在 = 之后的 - 与直接出现在 u、g 或 o 之后的 - 含义不同(在后一种情况下,它会移除特定的权限位,而不是全部权限)。
命令
同第六个
8 suid
每当用户想要执行只有 root 用户或具有 sudo 权限的用户才能完成的任务时,系统管理员不可能每次都在旁边为他们提供密码。“设置用户 ID”(SUID)权限位允许用户以程序文件所有者的身份运行程序。
hacker@dojo:~$ ls -l /usr/bin/sudo
-rwsr-xr-x 1 root root 232416 Dec 1 11:45 /usr/bin/sudo
hacker@dojo:~$
可执行位位置上的 s 部分意味着该程序以 SUID 权限可执行。这意味着,无论哪个用户运行该程序(只要他们具有执行权限),程序都将以所有者用户(在这种情况下是 root 用户)的身份执行。
作为文件所有者,你可以使用 chmod 设置文件的 SUID 位:chmod u + s [程序]
chmod u+s /challenge/getroot
/challenge/getroot
cat /flag
Untangling Users理清用户关系
在一个典型的 Linux 系统中有更多用户!Linux 系统上的完整用户列表在/etc/passwd文件中指定(之所以这样命名是出于历史原因 —— 实际上它不再存储密码)。
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:101:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
mysql:x:104:105:MySQL Server,,,:/nonexistent:/bin/false
messagebus:x:105:106::/nonexistent:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
hacker:x:1000:1000::/home/hacker:/bin/bash
这里有很多用户和大量信息!每行由冒号分隔,包含用户名、一个x作为曾经存放密码位置的占位符(我们稍后会讲到密码实际存放在哪里)、数字用户 ID、数字默认组 ID、用户详细信息、主目录和默认 shell。
很多用户存在是出于历史原因,有些是支持各种已安装软件的服务账户,还有一些是 “实用” 账户
1 su成为root用户
成为 root 用户是 Linux 用户相当常见的操作,有两个工具可用于此目的:su 和 sudo。
将介绍更古老的 su(切换用户命令)。如今它一般不再用于提升到 root 权限,但它是一个来自更有序时代的优雅工具,我们先来讲讲它。
su 是一个设置了 SUID 的二进制文件:
hacker@dojo:~$ ls -l /usr/bin/su
-rwsr-xr-x 1 root root 232416 Dec 1 11:45 /usr/bin/su
hacker@dojo:~$
因为设置了 SUID 位,su 会以 root 用户身份运行。以 root 用户身份运行时,它可以启动一个 root shell!当然,su 很谨慎:在允许用户将权限提升到 root 之前,它会检查以确保用户知道 root 密码:
hacker@dojo:~$ su
Password:
su: Authentication failure
hacker@dojo:~$
对 root 密码的这种检查正是 su 逐渐被淘汰的原因。现代系统很少设置 root 密码,而是使用不同的机制来授予管理权限。
su
hack-the-planet
cat /flag
2 其他用户su
如果不传入任何参数,su 会(在通过 root 密码验证后)启动一个 root shell。不过,你也可以传入一个用户名作为参数,从而切换到该用户而非 root 用户。
hacker@dojo:~$ su some-user
Password:
some-user@dojo:~$
su zardus
dont-hack-me
/challenge/run
3 john 破解密码
当你为 su 输入密码时,它会将输入的密码与该用户存储的密码进行比对。这些密码过去存储在 /etc/passwd 中,但由于 /etc/passwd 是一个全局可读文件,这对密码存储来说不安全,所以密码后来被转移到了 /etc/shadow 中。
root:$6$s74oZg/4.RnUvwo2$hRmCHZ9rxX56BbjnXcxa0MdOsW2moiW8qcAl/Aoc7NEuXl2DmJXPi3gLp7hmyloQvRhjXJ.wjqJ7PprVKLDtg/:19921:0:99999:7:::
daemon:*:19873:0:99999:7:::
bin:*:19873:0:99999:7:::
sys:*:19873:0:99999:7:::
sync:*:19873:0:99999:7:::
games:*:19873:0:99999:7:::
man:*:19873:0:99999:7:::
lp:*:19873:0:99999:7:::
mail:*:19873:0:99999:7:::
news:*:19873:0:99999:7:::
uucp:*:19873:0:99999:7:::
proxy:*:19873:0:99999:7:::
www-data:*:19873:0:99999:7:::
backup:*:19873:0:99999:7:::
list:*:19873:0:99999:7:::
irc:*:19873:0:99999:7:::
gnats:*:19873:0:99999:7:::
nobody:*:19873:0:99999:7:::
_apt:*:19873:0:99999:7:::
systemd-timesync:*:19901:0:99999:7:::
systemd-network:*:19901:0:99999:7:::
systemd-resolve:*:19901:0:99999:7:::
mysql:!:19901:0:99999:7:::
messagebus:*:19901:0:99999:7:::
sshd:*:19901:0:99999:7:::
hacker::19916:0:99999:7:::
zardus:$6$bEFkpM0w/6J0n979$47ksu/JE5QK6hSeB7mmuvJyY05wVypMhMMnEPTIddNUb5R9KXgNTYRTm75VOu1oRLGLbAql3ylkVa5ExuPov1.:19921:0:99999:7:::
每行以冒号分隔,第一个字段是用户名,第二个字段是密码。值为 * 或 ! 在功能上意味着该账户禁用密码登录,空白字段表示没有密码(这是一种并不少见的错误配置,在某些设置下允许无密码的 su 操作),像 Zardus 的 6 6 6bEFkpM0w/6J0n979$47ksu/JE5QK6hSeB7mmuvJyY05wVypMhMMnEPTIddNUb5R9KXgNTYRTm75VOu1oRLGLbAql3ylkVa5ExuPov1. 这样的长字符串,是对上一关 Zardus 密码(在这个例子中是 dont - hack - me)进行单向加密(哈希)的结果。
当你在 su 中输入密码时,它会对密码进行单向加密(哈希),并将结果与存储的值进行比较。如果结果匹配,su 就会授予你该用户的访问权限
但如果你不知道密码怎么办?如果你有密码的哈希值,就可以破解它!尽管默认情况下 /etc/shadow 只有 root 可以读取,但泄露情况仍有可能发生!
如果黑客获取了泄露的 /etc/shadow,他们就可以开始破解密码并制造破坏。可以通过著名的工具 John the Ripper 进行破解,如下所示:
hacker@dojo:~$ john./my - leaked - shadow - file
Loaded 1 password hash (crypt, generic crypt(3) [?/64])
Will run 32 OpenMP threads
Press 'q' or Ctrl - C to abort, almost any other key for status
password1337 (zardus)
1g 0:00:00:22 3/3 0.04528g/s 10509p/s 10509c/s 10509C/s lykys..lank
Use the "--show" option to display all of the cracked passwords reliably
Session completed
hacker@dojo:~$
john /challenge/shadow-leak
su zardus
aardvark
/challenge/run
sudo
与 su 不同,su 默认以指定用户身份启动一个 shell,而 sudo 默认以 root 用户身份运行一个命令:
hacker@dojo:~$ whoami
hacker
hacker@dojo:~$ sudo whoami
root
hacker@dojo:~$
与依赖密码认证的 su 不同,sudo 会检查策略,以确定用户是否有权以 root 用户身份运行命令。这些策略在 /etc/sudoers 中定义.
sudo cat /flag
Chaining Commands命令串联
1 (;)命令串联
串联命令最简单的方法是使用分号(;)。在大多数情况下,分号分隔命令的方式与回车键分隔各行的方式类似。所以,以下这组命令:
hacker@dojo:~$ echo COLLEGE > pwn; cat pwn
COLLEGE
hacker@dojo:~$
基本上,当你按下回车键时,你的 shell 会执行你输入的命令,在该命令终止后,会给出提示符让你输入另一个命令。分号的作用类似,只是没有提示符,并且你要在执行任何操作之前输入两个命令。
/challenge/pwn ; /challenge/college
2 shell脚本 bash (.sh结尾)
当你组合越来越多的命令以实现复杂效果时,组合后的命令行提示符长度会迅速变得冗长,处理起来相当恼人。遇到这种情况,你可以将这些命令放在一个文件中,这个文件被称为 shell 脚本,然后通过执行该文件来运行这些命令!
我们可以创建一个名为 pwn.sh
的 shell 脚本(按照惯例,shell 脚本通常以 .sh 作为后缀命名):
echo COLLEGE > pwn
cat pwn
然后,我们可以将其作为参数传递给一个新的 shell 实例(bash)来执行!当以这种方式调用 shell 时,它不会从用户处获取命令,而是从文件中读取命令。
hacker@dojo:~$ ls
hacker@dojo:~$ bash pwn.sh
COLLEGE
hacker@dojo:~$ ls
pwn
hacker@dojo:~$
在hacker文件里创建一个文本(x.sh)输入
/challenge/pwn
/challenge/college
保存
bash x.sh
3 重定向脚本输出
你已经用 | 在程序间传递输出,但这只是在一个命令的输出和另一个命令的输入之间进行。要是你想把几个程序的输出都发送到一个命令呢?有几种方法可以做到,我们在这里探讨一种简单的方法:重定向脚本的输出!
shell 而言,你的脚本不过是另一个命令。这意味着你可以像在 “管道” 模块中对普通命令那样,重定向它的输入和输出!例如,你可以将其输出写入一个文件:
hacker@dojo:~$ cat script.sh
echo PWN
echo COLLEGE
hacker@dojo:~$ bash script.sh > output
hacker@dojo:~$ cat output
PWN
COLLEGE
hacker@dojo:~$
重要
各种重定向方法都适用:> 用于标准输出,2> 用于标准错误输出,< 用于标准输入,>> 和 2>> 用于追加模式的重定向,>& 用于重定向到其他文件描述符,| 用于将输出通过管道传递给另一个命令。
bash x.sh | /challenge/solve
4 直接调用脚本
你已经编写了自己的第一个 shell 脚本,但通过 bash script.sh 来调用它很麻烦。为什么非要用 bash 呢?
当你调用 bash script.sh 时,当然是用 script.sh 作为参数 时,当然是用 script.sh 作为参数启动 bash 命令。这会告诉 bash 从 script.sh 读取命令,而不是从标准输入读取,这样你的 shell 脚本就会被执行。
其实你可以不用手动调用 bash。如果你的 shell 脚本文件是可执行的(回想一下文件权限相关知识),你只需通过其相对路径或绝对路径来调用它!例如,如果你在主目录中创建了 script.sh 并使其可执行,你可以通过 /home/hacker/script.sh 或 ~/script.sh 来调用它,或者(如果你的工作目录是 /home/hacker)通过 ./script.sh 来调用。
在/home/hacker里创建一个y.sh
写入/challenge/solve
chmod u+x y.sh
./y.sh
Pondering PATH 环境变量
你已经通过几种方式调用命令:
- 通过绝对路径(例如,/challenge/run)。
- 通过相对路径(例如,./run)。
- 通过单纯的命令名(例如,ls)。
前两种情况,即绝对路径和相对路径的情况,很简单明了:run 文件位于 /challenge 目录中,这两种情况都指向它,但最后一种情况呢?
1 PATH变量
shell 是如何找到 ls 命令的?” 有一个特殊的 shell 变量名为 PATH,它存储了一系列目录路径,shell 会在这些路径中搜索与命令对应的程序。
hacker@dojo:~$ ls
Desktop Downloads Pictures Templates
Documents Music Public Videos
hacker@dojo:~$ PATH=""
hacker@dojo:~$ ls
bash: ls: No such file or directory
hacker@dojo:~$
没有 PATH 变量,bash 就找不到 ls 命令。
PATH=“”
/challenge/run
2 设定PATH PATH=
如何将一个新的程序目录添加到我们的命令库中。要知道,PATH存储了一系列用于查找命令的目录,对于不在标准位置的命令,我们通常必须通过其路径来执行:
hacker@dojo:~$ ls /home/hacker/scripts
goodscript badscript okayscript
hacker@dojo:~$ goodscript
bash: goodscript: command not found
hacker@dojo:~$ /home/hacker/scripts/goodscript
YEAH! This is the best script!
hacker@dojo:~l
向这个列表中添加目录或替换其中的目录
hacker@dojo:~$ PATH=/home/hacker/scripts
hacker@dojo:~$ goodscript
YEAH! This is the best script!
hacker@dojo:~$
PATH=/challenge/more_commands
/challenge/run
3 添加命令(建议反复观看)
win 命令是 /challenge/run 唯一需要的,所以你可以只用那个目录覆盖 PATH。但要记住,如果你这么做,你的 win 命令将找不到 cat 命令。你有三种方法来避免这种情况:
- 弄清楚 cat 程序在文件系统中的位置。它肯定在 PATH 变量所包含的某个目录中,所以你可以打印出这个变量(参考 “Shell 变量” 回顾一下方法!),然后逐个查看其中的目录(回想一下,不同的目录项是用 : 分隔的),找到包含 cat 的目录,再通过其绝对路径调用 cat。
- 设置一个 PATH,既包含原来的目录,又为你创建 win 脚本的位置添加一个新的目录项。
- 使用 read 命令(同样,参考 “Shell 变量”)来读取 /flag。由于 read 是 bash 的内置功能,不受 PATH 变动的影响。
通过运行不带任何选项或参数(即输入数据)的 env 命令,可以看到当前用户的所有当前环境变量及其值的列表,包括 PATH 变量中的所有目录
添加不是覆盖的方法
将一个目录添加到用户的 PATH 变量中(从而将其添加到用户的默认搜索路径中)是一件简单的事情。对于当前会话,可以使用以下命令来完成,其中 “directory” 是要添加的目录的完整路径:
PATH="directory:$PATH"
答案:(查了好久资料,题目给的线索有点少感觉,最后看的官方答案)
mkdir -p /home/hacker/win
echo -e ‘#!/bin/bash\ncat /flag’ > /home/hacker/win/win
chmod +x /home/hacker/win/win
export PATH=/home/hacker/win:$PATH
/challenge/run
4 劫持命令
在 Linux 系统中,“劫持命令”(hijacking commands)通常指的是通过修改PATH环境变量或创建同名的可执行文件等方式,让系统在执行特定命令时,实际上执行你自定义的操作
mkdir -p /home/hacker/rm
echo -e ‘#!/bin/bash\ncat /flag’ > /home/hacker/rm/rm
chmod +x /home/hacker/rm/rm
export PATH=/home/hacker/rm:$PATH
(因为在PATH中~/rm在最前面,所以先执行这个rm)
/challenge/run
真不容易啊,又查了好多
完结撒花