loading

【入门笔记】pwn.college之Linux Luminarium

Hello Hackers

https://pwn.college/中的免费靶场,出自亚利桑那州立大学

本模块将教你与命令行交互的基础知识!命令行允许您执行命令。当您启动终端时,它将执行命令行 “shell”,如下所示:

hacker@dojo:~$

这称为 “prompt”,它会提示您输入命令。让我们来看看这里发生了什么:

提示中的hacker是当前用户的用户名。在 pwn.college网站的虚拟机DOJO环境中,默认用户就是“hacker”。

在上面的示例中,提示符的 dojo 部分是 shell 所在的机器的主机名(例如,如果您是每天处理许多机器的系统管理员,则此提醒可能很有用)。在上面的例子中,主机名是 dojo,但在 pwn.college 中,它将从你正在尝试的挑战的名称派生出来。

我们可以看看自己的虚拟机,你可以看到自己的主机名接在你的用户名后边

image-20241019191138269

我们稍后会介绍 ~ 的含义 :-)

提示符末尾的 $ 表示hacker不是管理用户。在 pwn.college 的后面的模块中,当你学会使用漏洞利用程序成为管理用户时,你会看到这里会通过回显 # 而不是 $ 来表示你的权限,然后你就会知道你成功了!

Intro to Commands 命令介绍

在这个挑战中,您将调用您的第一个命令!当您键入命令并按 Enter 键时,将调用该命令,如下所示:

hacker@dojo:~$ whoami
hacker
hacker@dojo:~$

这里,用户执行了 whoami 命令,该命令只是将用户名 (hacker) 打印到终端。当命令终止时,shell 将再次显示提示符,为下一个命令做好准备。

在这一关中,调用 hello 命令以获取flag!请记住:Linux 中的命令区分大小写:helloHELLO 不同。

查看解析
hello
我们直接输入"hello"即可调用"hello"命令

Intro to Argument 参数介绍

让我们尝试更复杂的方法:带有参数的命令,这就是我们所说的传递给命令的附加数据。当您键入一行文本并按 Enter 键时,shell 实际上会将您的输入解析为命令及其参数

第一个单词是命令,后面的单词是参数。示例如下:

hacker@dojo:~$ echo Hello
Hello
hacker@dojo:~$

在本例中,命令是 echo,参数是 Helloecho 是一个简单的命令,它将其所有参数都“回显”到终端上,就像您在上面的会话中看到的那样。

让我们看看具有多个参数的 echo

hacker@dojo:~$ echo Hello Hackers!
Hello Hackers!
hacker@dojo:~$

在本例中,命令是 echo,而 HelloHackers!echo 的两个参数。很简单对吧!

在此挑战中,要获取flag,您必须运行带有一个 hackers 参数的hello命令(不是 echo 命令)。现在就试试吧!

查看解析
hello hackers
我们在"hello"命令后接上"hackers"参数即可

Command History 命令历史

在终端中输入大量命令时,从头开始输入所有内容可能会很繁琐。幸运的是,Shell 会保存你调用过的每一条命令的历史记录。

你可以通过上下箭头键滚动查看这些已保存的命令,本次挑战中我们将练习这一操作。这个挑战会将标志(flag)注入到你的历史记录中。请打开终端,按上箭头键找到并获取它!在其他挑战中,历史记录将包含你运行过的命令日志,因此如果需要再次运行类似命令,你可以使用箭头键滚动查找!

查看解析
history
我们直接输入"history"即在历史命令中找到flag

Pondering Paths 思考路径

The Root 根

文件系统从 / 开始。在此之下,还有一大堆其他目录、配置文件、程序,最重要的是,还有flag。在这个关卡中,我们在 / 目录中添加了一个名为 pwn 的程序,它将为你提供flag。在这一关中,您需要做的就是调用此程序!

您可以通过在命令行上提供程序的路径来调用程序。在本例中,您将提供从 / 开始的确切路径,因此路径将为 /pwn。这种以根目录开头的路径样式称为 “绝对路径”。

开始挑战,启动一个终端,使用其绝对路径调用 pwn 程序,然后得到其中的flag!祝你好运!

我们可以观察自己虚拟机的文件结构,可以看出linux系统一切皆文件的理念

查看解析
/pwn
通过在"pwn"程序前加上"/"调用处于根目录的"pwn"程序

Program and absolute paths 程序的路径和绝对路径

让我们探索一条新的稍微复杂的路径!除了上一级之外,pwn.college 中的 很多关卡的挑战 都在 challenge 目录中,而 challenge 目录又在根目录 (/) 中。因此,目录的访问路径是 /challenge

此关卡中挑战的程序名称为 run,它位于 /challenge 目录中。因此,run 的程序路径是 /challenge/run

此挑战要求您通过调用程序的绝对路径来执行它。您需要执行 challenge 目录中的 run 文件,进而执行 / 目录中的运行文件。如果您正确调用了请求,它将为您提供flag。祝你好运!

查看解析
/challenge/run
通过输入"run"程序的绝对路径调用"run"程序

Position thy self 定位自己

Linux 文件系统有大量的目录和大量的文件。您可以使用 cdchange directory) 命令并将路径作为参数传递给命令,如下所示:

hacker@dojo:~$ cd /some/new/directory
hacker@dojo:/some/new/directory$ cd /some/new/directory

这会影响进程的 “当前工作目录” (在本例中为 bash shell) 。每个进程都有一个当前挂起的目录。其原因将在稍后的模块中阐明。

顺便说一句,现在你可以思考终端中的 ~ 是什么了!这一块显示的是 shell 所在的当前路径。

此挑战将要求您从特定路径执行 /challenge/run 程序(它会告诉您)。在重新运行挑战程序之前,您需要 cd 到该目录。祝你好运!

Bash shell是一种命令行界面和脚本语言,广泛用于Unix和Linux系统。它提供了用户与操作系统交互的方式,可以执行命令、运行脚本以及自动化任务

查看解析
/challenge/run
`回显路径`
cd `回显路径`
/challenge/run
通过"cd `回显路径`"跳转到指定路径执行程序

Position elsewhere 定位其他位置

与上一个挑战一样

Position yet elsewhere 再定位其他位置

与上一个挑战一样,重复题型加深记忆

implicit relative paths, from / 隐式相对路径,从/目录开始

现在,您已经熟悉了引用绝对路径和更改目录的概念。如果你到哪都输入绝对路径调用程序,那么你在哪个目录中真的无关紧要,你可能在前面的三个挑战中发现了这一点。

但是,当前工作目录对于相对路径确实很重要。

  • 相对路径是任何不以根开头的路径(即,它不以 / 开头)。
  • 相对路径英文是 current working directorycwd) 。
  • cwd 是显示当前所在的目录的提示符。

这意味着如何指定特定文件取决于终端提示符所在的位置

想象一下,我们想访问位于 /tmp/a/b/my_file 的某个文件

  • 如果我的 cwd/,那么文件的相对路径是 tmp/a/b/my_file
  • 如果我的 cwd/tmp,则文件的相对路径是 a/b/my_file
  • 如果我的 cwd/tmp/a/b/c,则文件的相对路径是 ../my_file.. 用于引用父目录。

父目录即上层目录

让我们在这里试试吧!您需要使用相对路径运行 /challenge/run,同时具有当前工作目录 /。在这一关中,我给你一个提示。您的相对路径需要以字母 c 开头

查看解析
cd /
challenge/run
为了使相对路径以字母c开头,我们在根目录下调用程序

explicit relative paths, from / 显式相对路径,从/目录开始

之前,你的相对路径是 “naked”:它直接指定了从当前目录进入到的下层目录。在这个关卡中,我们将探索更明确的相对路径。

在大多数操作系统(包括 Linux)中,每个目录都有两个隐式条目,您可以在 paths中引用它们:...

第一个.用于直接引用同一个目录,因此以下绝对路径彼此相同:

  • /challenge
  • /challenge/.
  • /challenge/./././././././././
  • /./././challenge/././

以下相对路径也彼此相同:

  • challenge
  • ./challenge
  • ./././challenge
  • challenge/.

当然,如果你现在的工作目录是 /,那么上面的相对路径相当于上面的绝对路径

做好准备,此挑战需要您在相对路径中使用 .

查看解析
cd /
./challenge/run
为了在相对路径中使用"."开头,我们在根目录下使用"./"调用程序

implicit relative 隐式相对路径

在这个关卡中,我们将练习使用 .更多地引用路径。此挑战需要您从 /challenge 目录运行它。在这里,事情变得有点棘手。

当您提供裸路径时,Linux 明确避免自动查找当前目录。请考虑以下事项:

hacker@dojo:~$ cd /challenge
hacker@dojo:/challenge$ run

不会调用 /challenge/run。这实际上是一种安全措施:如果 Linux 每次进入裸路径时都会在当前目录中搜索程序,你可能会不小心在当前目录中执行恰好与核心系统实用程序同名的程序!因此,上述命令将产生以下错误:

bash: run: command not found

我们稍后将探讨此概念背后的机制,但在此挑战中,将学习如何在此场景中显式使用相对路径来启动 run。执行此操作的方法是告诉 Linux 您明确希望在当前目录中执行一个程序,就像上一关一样。现在就试一试吧!

“裸路径”(naked path)指的是不包含任何目录结构或文件扩展名的路径,仅包含文件名。例如,文件名“example”就是一个裸路径,而“/home/user/example.txt”则不是。

查看解析
cd /challenge
./run
这一关旨在告诉我们在路径下直接输入程序名称不能调用程序,linux的安全机制阻止了

显式相对路径:明确指定了当前目录的相对位置,通常以当前工作目录作为起点。例如,如果当前工作目录是 /home/user,则文件 document.txt 的显式相对路径是 ./document.txt,或者直接用 document.txt 表示。

隐式相对路径:不直接指出当前工作目录,而是依赖于上下文或系统默认的路径。例如,直接使用 document.txt 就是一种隐式相对路径,系统会自动将其解析为当前工作目录下的文件。

home sweet home

每个用户都有一个主目录,通常在文件系统中的 /home 下。在 dojo虚拟机 中,您是 hacker 用户,您的主目录是 /home/hacker。主目录通常是用户存储大多数个人文件的位置。当您浏览 pwn.college 时,您将在主目录存储大部分文件。

通常,您的 shell 会话将以您的主目录作为当前工作目录开始。请考虑初始提示:

hacker@dojo:~$

此提示中的 ~ 是当前工作目录,其中 ~/home/hacker 的简写。Bash 提供并使用这种简写,因为同样,您的大部分时间将花在主目录中。因此,每当 bash 以与 path 一致的方式看到 ~ 作为参数的开头时,它就会将其扩展到您的主目录。考虑:

hacker@dojo:~$ echo LOOK: ~
LOOK: /home/hacker
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$

请注意,~ 的扩展是绝对路径,并且只有前导 ~ 被扩展。这意味着,例如,~/~ 将扩展为 /home/hacker/~,而不是 /home/hacker/home/hacker

有趣的事实:cd 将使用你的主目录作为默认目标(就是当你使用cd命令不传入任何参数时默认会跳转到主目录):

hacker@dojo:~$ cd /tmp
hacker@dojo:/tmp$ cd
hacker@dojo:~$

现在轮到您玩了!在此挑战中,/challenge/run 会将flag的副本写入您在命令行上的参数指定的文件,并具有以下条件:

  1. 您的参数必须是绝对路径。
  2. 该路径必须位于您的主目录中。
  3. 在扩展之前,您的参数必须为三个字符或更少。
查看解析
/challenge/run ~/a
按照题目提示"run"程序能够将flag的副本写入参数指定的文件,我们使用"~"代替绝对路径"/home/hacker"

Comprehending Commands 理解命令

cat: not the pet, but the command cat:不是宠物而是命令

最关键的 Linux 命令之一是 catcat 最常用于读出文件,如下所示:

hacker@dojo:~$ cat /challenge/DESCRIPTION.md
One of the most critical Linux commands is `cat`.
`cat` is most often used for reading out files, like so:

如果提供了多个参数,cat 将 concatenate(连接)多个文件。例如:

hacker@dojo:~$ cat myfile
This is my file!
hacker@dojo:~$ cat yourfile
This is your file!
hacker@dojo:~$ cat myfile yourfile
This is my file!
This is your file!
hacker@dojo:~$ cat myfile yourfile myfile
This is my file!
This is your file!
This is my file!

最后,如果你根本没有给出任何参数,cat 将从终端 input (输入)中读取并输出它。我们将在后面的挑战中探讨这一点......

在这个挑战中,我会将flag复制到你的主目录(你的 shell 开始的地方)的flag文件中。使用cat去读吧!

查看解析
cat flag
使用"cat"命令读取当前目录下的"flag"文件

catting absolute paths 使用绝对路径cat读取文件

在上一个关卡中,您执行了 cat flag 以从您的主目录中读取flag!当然,你可以将 cat 的参数指定为绝对路径:

hacker@dojo:~$ cat /challenge/DESCRIPTION.md
In the last level, you did `cat flag` to read the flag out of your home directory!
You can, of course, specify `cat`'s arguments as absolute paths:
...

在此挑战中,我不会将其复制到您的主目录,但会使其可读。你可以在它的绝对路径 /flag 中使用 cat 来读取它。

有趣的事实:/flag 在 pwn.college 的虚拟机中一直存在 ,但与本次挑战不同的是,您通常无法直接访问该文件。

查看解析
cat /flag
使用"cat"命令读取根目录下的"flag"文件

more catting practice 更多cat读取练习

你可以指定各种路径作为命令的参数,我们将使用 cat 进行更多练习。在这个关卡中,我将把flag放在某个疯狂的目录中,我不允许你用 cd 来改变目录,所以别想着用 cat flag。您必须通过绝对路径检索flag,无论它位于何处。

查看解析
cat "flag的绝对路径"
"flag的绝对路径"在打开终端的那一刻给出

grepping for a needle in a haystack 大海捞针

有时,您可能会发现cat的文件太大。幸运的是,我们有 grep 命令来搜索我们需要的内容!我们将在这次挑战中学习。

grep 的方法有很多种,我们将在以下示例中学习:

hacker@dojo:~$ grep SEARCH_STRING /path/to/file

像这样调用,grep 将在文件中搜索包含 SEARCH_STRING 的文本行,并将它们打印到控制台。

在这次挑战中,我在 /challenge/data.txt 文件中放入了 10 万行文本。使用 grep 寻找flag!

提示: flag总是以 pwn.college 开头。

查看解析
grep pwn.college /challenge/data.txt
使用"grep"命令搜索包含“pwn.college”的文本行

comparing files 文件对比

在比较相似文件之间的差异时,仅靠肉眼观察可能并非最高效的方法!这时 diff 命令就显得不可或缺。

diff 会逐行比较两个文件,并精确显示它们之间的差异。例如:

hacker@dojo:~$ cat file1
hello
world
hacker@dojo:~$ cat file2
hello
universe
hacker@dojo:~$ diff file1 file2
2c2
< world
---
> universe

输出结果告诉我们第 2 行发生了变更(2c2),其中第一个文件(<)中的 world 被第二个文件(>)中的 universe 替换了。

有时,当添加新行时,你会看到类似这样的输出:

hacker@dojo:~$ cat old
pwn
hacker@dojo:~$ cat new
pwn
college
hacker@dojo:~$ diff old new
1a2
> college

这表示在第一个文件的第 1 行之后,第二个文件多出了一行(1a2 的含义是“在文件1的第 1 行后添加文件2的第 2 行”)。

现在轮到你的挑战了!在 /challenge 目录中有两个文件:

  • /challenge/decoys_only.txt 包含 100 个假flag
  • /challenge/decoys_and_real.txt 包含全部 100 个假flag以及一个真实flag

使用 diff 找出这两个文件之间的差异,从而获取你的flag!

查看解析
diff /challenge/decoys_only.txt /challenge/decoys_and_real.txt
使用`diff`命令进行文件对比

listing files 列出文件

到目前为止,我们已经告诉您要与哪些文件进行交互。但是目录里面可以有很多文件(和其他目录),我们不会总告诉你它们的名字。您需要学习使用 ls 命令来 list 它们的内容!

ls 将列出提供给它的所有目录中的文件,如果未提供参数,则列出当前目录中的文件。观察以下示例:

hacker@dojo:~$ ls /challenge
run
hacker@dojo:~$ ls
Desktop    Downloads  Pictures  Templates
Documents  Music      Public    Videos
hacker@dojo:~$ ls /home/hacker
Desktop    Downloads  Pictures  Templates
Documents  Music      Public    Videos
hacker@dojo:~$

在这个挑战中,我们用一些随机名称命名了 /challenge/run!列出 /challenge 中的文件以查找它。

查看解析
ls /challenge
`run程序的新名字`
/challenge/`run程序的新名字`
使用"ls"命令查看"/challenge"目录下的“run”程序的名称并运行

touching files 创建文件

当然,您也可以创建文件!有几种方法可以做到这一点,但我们在这里看一个简单的命令。您可以通过使用 touch 命令触摸来创建新的空白文件:

hacker@dojo:~$ cd /tmp
hacker@dojo:/tmp$ ls
hacker@dojo:/tmp$ touch pwnfile
hacker@dojo:/tmp$ ls
pwnfile
hacker@dojo:/tmp$

就这么简单!在这个关卡中,请创建两个文件:/tmp/pwn/tmp/college,然后运行 /challenge/run 来获取你的flag!

查看解析
touch /tmp/pwn
touch /tmp/college
/challenge/run
使用"touch"命令创建文件

removing files 删除文件

文件就在你身边。就像糖果包装纸一样,它们最终会太多。在这个关卡中,我们将学习清理它们!

在 Linux 中,使用 rm 命令可以删除文件,如下所示:

hacker@dojo:~$ touch PWN
hacker@dojo:~$ touch COLLEGE
hacker@dojo:~$ ls
COLLEGE     PWN
hacker@dojo:~$ rm PWN
hacker@dojo:~$ ls
COLLEGE
hacker@dojo:~$

让我们练习一下。此挑战在您的主目录中创建了一个 delete_me 文件!删除它,然后运行 /challenge/check,这将确保您已删除它,然后为您提供flag!

查看解析
rm delete_me
/challenge/check
使用"rm"命令删除文件

moving files 移动文件

你也可以使用 mv 命令来移动文件。用法很简单:

hacker@dojo:~$ ls
my-file
hacker@dojo:~$ cat my-file
PWN!
hacker@dojo:~$ mv my-file your-file
hacker@dojo:~$ ls
your-file
hacker@dojo:~$ cat your-file
PWN!
hacker@dojo:~$

这个挑战要求你将 /flag 文件移动到 /tmp/hack-the-planet/ 目录中(请完成这个操作!)。完成后,运行 /challenge/check,它会检查操作结果并给你flag。

查看解析
mv /flag /tmp/hack-the-planet
/challenge/check
将`/flag`文件移动到`/tmp/hack-the-planet`

copying files 复制文件

但如果想要保留原始文件该怎么办呢?你可以使用 cp 命令来实现。其用法与 mv 命令相同,但 cp 会保留源文件。

这个挑战要求你将 /flag 文件复制到 /tmp/hack-the-planet/ 目录中(请完成这个操作!)。完成后,运行 /challenge/check,它会检查操作结果并给你flag。

查看解析
cp /flag /tmp/hack-the-planet
/challenge/check
将`/flag`文件移动到`/tmp/hack-the-planet`

hidden files 隐藏文件

有趣的是,ls 默认情况下不会列出所有文件。Linux 有一个约定,默认情况下,以 .开头的文件不会显示在 ls 和其他一些上下文中。要使用 ls 查看它们,您需要使用 -a flag调用 ls,如下所示:

hacker@dojo:~$ touch pwn
hacker@dojo:~$ touch .college
hacker@dojo:~$ ls
pwn
hacker@dojo:~$ ls -a
.college	pwn
hacker@dojo:~$

现在,轮到你了!找到那个flag,它隐藏在 / 中是一个带点的文件。

查看解析
ls -a /
使用"ls -a"命令查看根目录的隐藏文件

An Epic Filesystem Quest 史诗级的文件系统任务

凭借您对 cdlscat 的了解,我们已经准备好玩一个小游戏了!

我们将从 / 开始。如下所示:

hacker@dojo:~$ cd /
hacker@dojo:/$ ls
bin   challenge  etc   home  lib32  libx32  mnt  proc  run   srv  tmp  var
boot  dev        flag  lib   lib64  media   opt  root  sbin  sys  usr

内容真是太多了!总有一天,你会对它们非常熟悉,但你可能已经认出了flag文件和challenge关卡挑战目录。

在这次挑战中,我隐藏了flag!在这里,您将使用 lscat 来跟踪我的线索并找到它!以下是它的工作原理:

  1. 你的第一个线索在 / 中。向那边走。
  2. ls 环顾四周。将会有一个名为 HINT 或 CLUE 或类似名称的文件!
  3. cat那个文件来读线索!
  4. 根据线索所说的内容,前往下一个目录(或者不要!
  5. 循线索到flag!

祝你好运!

查看解析
灵活运用"cd""ls""cat"三条指令即可< /challenge/read_me

making directories 创建目录

我们可以创建文件。目录可以创建吗?你可以使用mkdir命令来创建目录,然后你就可以把文件贴进去了!

这里给出实例:

hacker@dojo:~$ cd /tmp
hacker@dojo:/tmp$ ls
hacker@dojo:/tmp$ ls
hacker@dojo:/tmp$ mkdir my_directory
hacker@dojo:/tmp$ ls
my_directory
hacker@dojo:/tmp$ cd my_directory
hacker@dojo:/tmp/my_directory$ touch my_file
hacker@dojo:/tmp/my_directory$ ls
my_file
hacker@dojo:/tmp/my_directory$ ls /tmp/my_directory/my_file
/tmp/my_directory/my_file
hacker@dojo:/tmp/my_directory$

现在创建一个 /tmp/pwn 目录并在其中创建名为college的文件!然后运行 /challenge/run,它将检查您的答案并为您提供flag!

查看解析
mkdir /tmp/pwn
touch /tmp/pwn/college
/challenge/run

finding files 查找文件

现在我们知道如何列出、读取和创建文件了。但是我们如何查找它们呢?我们使用 find 命令!

find 命令采用描述搜索条件和搜索位置的可选参数。如果未指定搜索条件,则 find 将匹配每个文件。如果未指定搜索位置,则 find 将使用当前工作目录 (.)。例如:

hacker@dojo:~$ mkdir my_directory
hacker@dojo:~$ mkdir my_directory/my_subdirectory
hacker@dojo:~$ touch my_directory/my_file
hacker@dojo:~$ touch my_directory/my_subdirectory/my_subfile
hacker@dojo:~$ find
.
./my_directory
./my_directory/my_subdirectory
./my_directory/my_subdirectory/my_subfile
./my_directory/my_file
hacker@dojo:~$

指定搜索位置时:

hacker@dojo:~$ find my_directory/my_subdirectory
my_directory/my_subdirectory
my_directory/my_subdirectory/my_subfile
hacker@dojo:~$

当然,我们可以指定标准!例如,在这里我们使用-name参数按名称进行筛选:

hacker@dojo:~$ find -name my_subfile
./my_directory/my_subdirectory/my_subfile
hacker@dojo:~$ find -name my_subdirectory
./my_directory/my_subdirectory
hacker@dojo:~$

如果你愿意,你可以在整个文件系统中搜索!

hacker@dojo:~$ find / -name hacker
/home/hacker
hacker@dojo:~$

现在轮到你了。我已将flag隐藏在文件系统上的随机目录中。它仍然被称为 flag。快去找吧!

几个笔记:首先,文件系统上还有其他名为 flag 的文件。如果您尝试的第一个没有实际的flag,请不要惊慌。其次,文件系统中有很多地方是普通用户无法访问的。这些将导致 find 生成错误,但您可以忽略这些错误;我们不会把flag藏在那里!最后,find可能需要一段时间;请耐心等待!

查看解析
find / -name flag
使用"find"命令查找整个文件系统中名为"flag"的文件

linking flag 链接文件

如果您使用 Linux(或计算机)进行任何合理的时间来执行任何实际工作,您最终可能会遇到以下情况:您希望两个程序访问相同的数据,但程序希望该数据位于两个不同的位置。 幸运的是,Linux 为这个困境提供了一个解决方案:链接

链接有两种类型:链接和(也称为符号)链接。 我们将通过一个比喻来区分两者:

链接是指您使用多个地址来称呼您的公寓,这些地址都直接指向同一个地方(例如,岭南1栋南雅1栋是同一栋楼)。

链接是指您移动公寓并修改快递地址,快递公司自动将您的快递从旧地方转发到新地方。

在文件系统中,从概念上讲,文件是该文件内容所在的地址。 硬链接是一个备用地址,它索引该数据---对硬链接的访问和对原始文件的访问是完全相同的,因为它们会立即产生必要的数据。 而软/符号链接则包含原始文件名。 当您访问符号链接时,Linux 将意识到它是一个符号链接,读取原始文件名,然后(通常)自动访问该文件。 在大多数情况下,这两种情况都会导致访问原始数据,但机制不同。

硬链接对大多数人来说听起来更简单(例如,我在上面的一句话中解释了它,而软链接则有两句),但它们有各种缺点和实现问题,这使得软/符号链接成为迄今为止更流行的选择。

在这个挑战中,我们将学习符号链接(也就是软链接)。 符号链接是使用带有 -s 参数的 ln 命令创建的,如下所示:

hacker@dojo:~$ cat /tmp/myfile
This is my file!
hacker@dojo:~$ ln -s /tmp/myfile /home/hacker/ourfile
hacker@dojo:~$ cat ~/ourfile
This is my file!
hacker@dojo:~$

你可以看到,访问符号链接会导致获取原始文件内容! 此外,您还可以查看 ln -s 的用法。 请注意,原始文件路径位于命令中的链接路径之前

可以通过几种方法识别符号链接。 例如,file 命令(它接受文件名并告诉你它是什么类型的文件)将识别符号链接:

hacker@dojo:~$ file /tmp/myfile
/tmp/myfile: ASCII text
hacker@dojo:~$ file ~/ourfile
/home/hacker/ourfile: symbolic link to /tmp/myfile
hacker@dojo:~$

好了,现在你试试吧! 在这个关卡中,flag一如既往地在 /flag 中,但 /challenge/catflag 程序将读取 /home/hacker/not-the-flag文件。 使用符号链接,并欺骗它给你flag!

文件A -----> 数据块X

硬链接1 ---> 数据块X
硬链接2 ---> 数据块X

软链接1 -----> 文件A

查看解析
ln -s /flag /home/hacker/not-the-flag 
/challenge/catflag
使用"ln -s"命令将"/flag"与"/home/hacker/not-the-flag"进行软链接,这样当"/challenge/catflag"程序读取"/home/hacker/not-the-flag"文件时会被链接读取到"/flag"文件

Digesting Documentation 摘要文档

Learning From Documentation 从文档中学习

我们使用文档只是想要弄清楚如何使用所有这些程序,其中一个具体的例子就是弄清楚在命令行上指定什么参数。本模块将主要深入研究这个概念,作为了解如何一般使用这些程序的代理。在本模块的其余部分中,您将了解向环境寻求程序帮助的各种方法,但首先,我们将深入了解阅读文档的概念。

程序的正确使用在很大程度上取决于对它们的参数的正确规范。回想一下基本命令模块的隐藏文件查询中指令 ls -a-a

-a 是一个参数,它告诉 ls 列出隐藏文件和非隐藏文件。因为我们想要列出隐藏的文件,所以在我们的场景中使用 ls-a 参数是正确的使用它的方法。

此挑战的程序是 /challenge/challenge,您需要正确调用它才能为您提供flag。让我们假设这是它的文档:

Welcome to the documentation for `/challenge/challenge`! To properly run this program, you will need to pass it the argument of `--giveflag`. Good luck!
欢迎使用 /challenge/challenge 的文档!要正确运行此程序,您需要向其传递 --giveflag 的参数。祝你好运!

有了这些知识,就去获取这题的flag吧!

查看解析
/challenge/challenge --giveflag
我们通过题目给出的提示文档得知我们需要给"challenge"程序传入"--giveflag"的参数

Learning Complex Usage 学习复杂用法

虽然使用大多数命令很简单,但某些命令的使用可能会变得相当复杂。例如,像 sedawk 这样的命令的参数(我们现在先不讨论这些)是用深奥的编程语言编写的整个程序!在 cdawk 之间的某些地方是将参数转换为其参数的命令......

这听起来很疯狂,但您已经在 基本命令 中的 find 关卡遇到过这种情况。find 有一个 -name 参数,而 -name 参数本身需要一个参数,指定要搜索的名称。许多其他命令都是类似的。

以下是 /challenge/challenge 的本层级文档:

Welcome to the documentation for /challenge/challenge! This program allows you to print arbitrary files to the terminal, when given the --printfile argument. The argument to the --printfile argument is the path of the flag to read. For example, /challenge/challenge --printfile /challenge/DESCRIPTION.md will print out the description of the level!

欢迎使用 /challenge/challenge 的文档!当给定 --printfile 参数时,该程序允许您将任意文件打印到终端。--printfile 参数的参数是要读取的flag的路径。例如, /challenge/challenge --printfile /challenge/DESCRIPTION.md 将打印出关卡的描述!

有了这个文档,就去获取flag吧!

查看解析
/challenge/challenge --printfile /flag
按照文档指示使用"challenge"程序读取"/flag"

Reading Manuals 阅读手册

此关卡介绍了 man 命令。manmanual 的缩写,它将显示(如果可用的话)您作为参数传递的命令的手册。例如,假设我们想了解 yes 命令(是的yes是一个真正的命令,不信你试试):

hacker@dojo:~$ man yes

这将显示 yes 的手册页,如下所示:

YES(1)                           User Commands                          YES(1)

NAME
       yes - output a string repeatedly until killed

SYNOPSIS
       yes [STRING]...
       yes OPTION

DESCRIPTION
       Repeatedly output a line with all specified STRING(s), or 'y'.

       --help display this help and exit

       --version
              output version information and exit

AUTHOR
       Written by David MacKenzie.

REPORTING BUGS
       GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
       Report any translation bugs to <https://translationproject.org/team/>

COPYRIGHT
       Copyright  ©  2020  Free Software Foundation, Inc.  License GPLv3+: GNU
       GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
       This is free software: you are free  to  change  and  redistribute  it.
       There is NO WARRANTY, to the extent permitted by law.

SEE ALSO
       Full documentation <https://www.gnu.org/software/coreutils/yes>
       or available locally via: info '(coreutils) yes invocation'

GNU coreutils 8.32               February 2022                          YES(1)

重要的部分包括:

NAME(1)                           CATEGORY                          NAME(1)

NAME
	This gives the name (and short description) of the command or
	concept discussed by the page.

SYNOPSIS
	This gives a short usage synopsis. These synopses have a standard
	format. Typically, each line is a different valid invocation of the
	command, and the lines can be read as:

	COMMAND [OPTIONAL_ARGUMENT] SINGLE_MANDATORY_ARGUMENT
	COMMAND [OPTIONAL_ARGUMENT] MULTIPLE_ARGUMENTS...

DESCRIPTION
	Details of the command or concept, with detailed descriptions
	of the various options.

SEE ALSO
	Other man pages or online resources that might be useful.

COLLECTION                        DATE                          NAME(1)

您可以使用箭头键和 PgUp/PgDn 在手册页中滚动。阅读完手册页后,可以按 q 退出。

手册页存储在集中式数据库中。如果您好奇,这个数据库位于 /usr/share/man 目录中,但您永远不需要直接与它交互:您只需使用 man 命令查询它。man 命令的参数不是文件路径,而只是条目本身的名称(例如,您运行 man yes 来查找 yes 手册页,而不是 man /usr/bin/yes,这将是 yes 程序的实际路径,但会导致 man 显示垃圾)。

此关卡的挑战有一个秘密选项,当您使用它时,将导致挑战打印flag。您必须通过 challenge 的手册页来了解此选项

查看解析
man /challenge/challenge
我们通过"man"命令查看challenge"程序的使用手册

Searching Manuals 搜索手册

您可以使用箭头键(和 PgUp/PgDn)滚动手册页,并使用 / 进行搜索。搜索后,您可以按 n 转到下一个结果,按 N 转到上一个结果。你可以使用 ?来向后搜索!

通过阅读 challenge 的使用手册找到为您提供flag的选项

查看解析
man /challenge/challenge
我们通过"man"命令查看"challenge"程序的使用手册

Searching For Manuals 搜索“搜索”的使用手册

这个关卡很棘手:它通过随机化其名称来隐藏挑战的手册页。幸运的是,所有的手册页都收集在一个可搜索的数据库中,因此您将能够搜索手册页数据库以找到隐藏的挑战手册页!要了解如何搜索正确的手册页,请通过执行以下操作阅读手册页:man man

提示 1:man man 教你 man 命令本身的高级用法,你必须利用这些知识来弄清楚如何搜索隐藏的 manpage,它会告诉你如何使用 /challenge/challenge

提示 2:虽然手册页是随机命名的,但您实际上仍然使用 /challenge/challenge 来获取flag!

查看解析
man -K /challenge/challenge
我们通过"man -K"命令搜索手册页中包含“/challenge/challenge”这个字符串的所有条目(因为权限问题难以直接读取"challenge"程序的使用手册)

Helpful Programs 有帮助的程序

有些程序没有手册页,但如果使用特殊参数调用,可能会告诉您如何运行它们。通常,此参数是 --help,但通常可以是 -h,或者在极少数情况下是 -?help 或其他深奥的值,如 /?(尽管后者在 Windows 上更常见)。

在这个关卡中,你将练习使用 --help 阅读程序的文档。试试看!

查看解析
/challenge/challenge --help

Help for Builtins 内置软件的帮助功能

某些命令(不是带有手册页和帮助选项的程序)内置在 shell 本身中。这些称为 builtin。内置函数的调用方式与命令类似,但 shell 在内部处理它们,而不是启动其他程序。你可以通过运行内置帮助来获取 shell 内置函数的列表,如下所示:

hacker@dojo:~$ help

您可以通过将特定帮助传递给内置 help 来获得该帮助。让我们看看我们之前已经使用过的 builtin,cd

hacker@dojo:~$ help cd
cd: cd [-L|[-P [-e]] [-@]] [dir]
    Change the shell working directory.
    
    Change the current directory to DIR.  The default DIR is the value of the
    HOME shell variable.
...

这有一些好信息!在这个挑战中,我们将练习使用 help 来查找内置函数的帮助。此挑战的 challenge 命令是内置的 shell,而不是程序。和以前一样,你需要查找它的帮助以找出要传递给它的 secret 值!

查看解析
help challenge

File Globbing 文件通配

Matching with * 与*匹配

我们将学习的第一个通配符是 *。当它在任何参数中遇到 * 字符时,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

当然,尽管在这种情况下,glob 导致了多个参数,但它也可以简单地只匹配一个。例如:

hacker@dojo:~$ touch file_a
hacker@dojo:~$ ls
file_a
hacker@dojo:~$ echo Look: file_*
Look: file_a

当匹配到零个文件时,默认情况下,shell 会保持 glob 不变:

hacker@dojo:~$ touch file_a
hacker@dojo:~$ ls
file_a
hacker@dojo:~$ echo Look: nope_*
Look: nope_*

* 匹配文件名中除 / 或前导 . 字符之外的任何部分(也就是说不能像这样匹配:*flag->/flag*flag->.flag)。例如:

hacker@dojo:~$ echo ONE: /ho*/*ck*
ONE: /home/hacker
hacker@dojo:~$ echo TWO: /*/hacker
TWO: /home/hacker
hacker@dojo:~$ echo THREE: ../*
THREE: ../hacker

现在,自己练习吧!从你的主目录开始,将你的目录改为 /challenge,请使用通配符将你传递给 cd 的参数在四个字符以内!达成条件后,运行 /challenge/run 以获取flag!

查看解析
cd /ch*
./run
我们使用通配符"*"将"/ch*"匹配为"/challenge"(以"ch"开头的文件是独一无二的"challenge",不能是其他的)

Matching with ? 与?匹配

接下来,让我们了解 ?。当它在任何参数中遇到 ?字符时,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

现在,自己练习吧!从您的主目录开始,将您的目录更改为 /challenge,但在 cd 的参数中使用 ?字符来替代 cl!达成条件后,运行 /challenge/run 以获取flag!

查看解析
cd /?ha??enge
./run
我们使用通配符"?"将"/?ha??enge"匹配为"/challenge"

Matching with [] 与[]匹配

接下来,我们将介绍 []。方括号实质上是 ?的有限形式,因为 [] 不是匹配任何字符,而是在方括号内指定的某些潜在字符子集的通配符。例如,[pwn] 将匹配字符 pwn。例如:

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

在这里试试吧!我们在 /challenge/files 中放置了一堆文件。将工作目录更改为 /challenge/files,并使用一个参数运行 /challenge/run,使用[]file_bfile_afile_sfile_h 作为参数输入!

查看解析
cd /challenge/files
../run file_[bash]
我们使用通配符"[]"将"file_[bash]"匹配为`file_b`、`file_a`、`file_s`、`file_h`四个文件

Matching paths with [] 使用[]匹配路径

通配是基于路径发生的,因此你可以使用通配参数扩展整个路径。例如:

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: /home/hacker/file_[ab]
Look: /home/hacker/file_a /home/hacker/file_b

现在轮到你了。我们再次在 /challenge/files 中放置了一堆文件。从您的主目录开始,使用单个参数运行 /challenge/run,该参数将 file_bfile_afile_sfile_h 文件的绝对路径括起来!

查看解析
/challenge/run /challenge/files/file_[bash]
我们使用通配符"[]"将"file_[bash]"匹配为`file_b`、`file_a`、`file_s`、`file_h`四个文件

Multiple globs 多重通配符

到目前为止,您每次只使用一个通配符,但其实您可以做得更多!Bash 支持在单个单词中扩展多个通配符。例如:

hacker@dojo:~$ cat /*fl*
pwn.college{YEAH}
hacker@dojo:~$

上述命令执行的过程是:Shell 会在根目录 / 下查找所有以任意字符(包括空字符)开头,紧接着包含 fl,并以任意字符(包括 ag,这样就组成了 flag)结尾的文件。

现在请您来尝试一下。我们在 /challenge/files 目录下放置了几个文件名多样但都令人愉快的文件。请先通过 cd 命令进入该目录,然后运行 /challenge/run,并提供一个参数:一个简短(3个字符或更少)的、包含两个 * 通配符的单词,该通配模式需要能匹配到所有包含字母 p 的单词。

查看解析
cd /challenge/files
/challenge/run *p*
`*p*`以匹配到**所有包含字母 `p`** 的单词

Mixing globs 混合globs

现在,让我们把前面的关卡放在一起吧!我们在 /challenge/files 中放置了一些 Happy 但名称不同的文件。去 cd 那里,使用你学到的 globing,编写一个简短的(6 个字符或更少)的 glob,它将匹配 “challing”、“academical” 和 “pwning” 文件!

查看解析
cd /challenge/files
../run  [cap]*
我们使用通配符"[]"和"*"将"[cap]*"匹配为`challing`、`academical`、`pwning`四个文件

Exclusionary globbing 排他性通配

有时,您想过滤掉 glob 中的文件!幸运的是,[] 可以帮助你做到这一点。如果括号中的第一个字符是 或着^^用于较新版本的 bash 中),则 glob 将反转,并且该括号实例匹配列出的字符。例如:

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

有了这些知识,就转到 /challenge/files 并对所有不以 pwn 开头的文件运行 /challenge/run

注意:字符不是 [] glob 的第一个字符时,它在 bash 中具有不同的特殊含义,因此如果事情不再有意义,请记住这一点!^ 没有这个问题,但也不兼容旧的 shell

查看解析
cd /challenge/files
../run  [!pwn]*
我们使用通配符"[!]"和"*"将"[!pwn]*"匹配为开头不为`p`、`w`、`n`的文件

Tab completion Tab 键补全

尽管使用 * 来缩短命令行输入很诱人,但这可能导致错误。你的通配符可能会扩展到意料之外的文件,而你可能直到 rm 命令已经开始执行才发现这一点!任何人都可能犯这类错误。

当你试图指定一个特定目标时,一个更安全的替代方案是使用 Tab 键补全。如果你在 Shell 中按下 Tab 键,它会尝试推测你将要输入的内容并自动补全。自动补全功能非常实用,本挑战将探索其在指定文件时的用法。

此挑战已将flag复制到 /challenge/pwncollege 文件中,你可以随意使用 cat 查看该文件。但你不能手动输入文件名:我们使用了一些巧妙的手段来确保你必须使用 Tab 键补全。试试看吧!

hacker@dojo:~$ ls /challenge
DESCRIPTION.md  pwncollege
hacker@dojo:~$ cat /challenge/pwncollege
cat: /challenge/pwncollege: No such file or directory
hacker@dojo:~$ cat /challenge/pwn<TAB>
pwn.college{太棒了}
hacker@dojo:~$

当你按下 Tab 键时,文件名会自动补全,你就能读取文件了。祝你好运!

查看解析
cat /challenge/pwn《TAB键》

Multiple options for tab completion 多选项时的 Tab 键补全

考虑以下情况:

hacker@dojo:~$ ls
flag  flamingo  flowers
hacker@dojo:~$ cat f<TAB>

存在多个匹配选项!会发生什么?

具体行为会根据使用的 Shell 及其配置而有所不同。在默认的 bash 中,当存在多个选项时,它会自动扩展到第一个产生分歧的字符(在这个例子中,是扩展到 fl)。当你再次按下 Tab 键时,它会列出所有这些可选项。而在其他 Shell 或不同配置下,行为可能会变成在所有选项之间循环选择。

这个挑战在 /challenge/files 目录下存放了一系列以 pwncollege 开头的文件。请尝试从输入 /challenge/files/p 开始使用 Tab 键补全,逐步找到并获取flag文件!

查看解析
cat /challenge/pwn《TAB键》

Tab completion on commands Tab 键补全命令

Tab 键补全不仅限于文件路径!你也可以用它来补全命令。本关卡有一个以 pwncollege 开头的命令,运行它即可获得flag。只需输入 pwncollege 然后按下 Tab 键即可自动补全该命令!

注意:虽然你可以自动补全任何命令,但请务必谨慎:如果不仔细核对补全结果就贸然执行,可能会因为意外运行了错误的命令而对你的 Shell 环境造成严重破坏!

查看解析
pwncollege《TAB键》

Practicing Piping 练习管道

Redirecting output 重定向输出

首先,让我们看看将 stdout 重定向到文件。您可以使用 > 字符完成此操作,如下所示:

hacker@dojo:~$ echo hi > asdf

这会将 echo hi 的输出(即 hi)重定向到文件 asdf。然后,你可以使用 cat 等程序输出此文件:

hacker@dojo:~$ cat asdf
hi

在此挑战中,您必须使用此输入重定向将单词 PWN(全部大写)写入文件名 COLLEGE(全部大写)

查看解析
echo PWN > COLLEGE
我们使用"echo"将"PWN"作为输出流,并使用">"将输出流写入"COLLEGE"文件中

stdout 是标准输出流(Standard Output)的缩写,我们将它视为终端的输出回显即可

Redirecting more output 重定向更多输出

除了重定向 echo 的输出之外,您当然还可以重定向任何命令的输出(比如printcat)。在这个关卡中,/challenge/run 将再次给你一个flag,但前提是你将其输出重定向到文件 myflag中。当然,您的flag最终会重定向出现在 myflag 文件中!

你会注意到尽管你重定向了 stdout ,/challenge/run 仍然会有输出打印到你的终端。那是因为它通过标准错误(stderr)来传达其指令和反馈,并且只在stdout上打印flag!

stderr 是标准错误流(Standard Error)的缩写,用于输出错误消息或诊断信息。

查看解析
/challenge/run > myflag
cat myflag
我们使用">"将"/challenge/run"输出流写入"COLLEGE"文件中

image-20241020163401388

Appending output 附加输出

输出重定向的一个常见用例是保存一些命令结果以供以后分析。通常,您希望汇总执行此操作:运行一堆命令,保存其输出,并在以后 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。这种做法将把flag的前半部分写入文件,如果将标准输出重定向到文件,则将后半部分写入标准输出。如果您以追加模式正确地重定向,则后半部分将追加到第一部分,但如果您以截断模式(>)重定向,则后半部分将覆盖第一部分,并且您将无法获得flag!(此时请重启靶机

现在开始吧!

查看解析
/challenge/run >> the-flag
cat the-flag
我们使用">>"将"/challenge/run"输出流附加写入"the-flag"文件中

Redirecting errors 重定向错误

就像标准输出一样,您也可以重定向命令的错误通道。在这里,我们将学习文件描述符编号。文件描述符(FD)是Linux中描述通信通道的数字。你已经在使用它们了,尽管你没有意识到。我们已经熟悉了三个:

  • FD 0: Standard Input 标准输入
  • FD 1: Standard Output 标准输出
  • FD 2: Standard Error 标准误差

当您重定向进程通信时,您可以通过 FD 编号来执行此操作,尽管某些 FD 编号是隐式的。例如,没有数字的 > 表示 1>,这会重定向 FD 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

然后将 “errors”(即标准错误输出)重定向到 instructions。您会注意到不会将任何内容打印到终端,因为您已经重定向了所有内容!当您成功完成此操作时,您可以在instructions中找到说明/反馈,并在 myflag 中找到flag!

查看解析
/challenge/run > myflag 2> instructions
cat instructions
cat myflag
我们使用"2>"将标准错误输出流写入"instructions"文件中

Redirecting input 重定向输入

就像您可以重定向程序的输出一样,您也可以将输入重定向到程序!这是使用 < 完成的,如下所示:

hacker@dojo:~$ echo yo > message
hacker@dojo:~$ cat message
yo
hacker@dojo:~$ rev < message
oy

您可以使用输入重定向在许多不同的程序中做有趣的事情!在这个关卡中,我们将练习使用 /challenge/run,这将需要您将 PWN 文件重定向到它,并让 PWN 文件包含值 COLLEGE!要将该值写入 PWN 文件,请回想一下之前对 echo 的输出重定向的挑战!

查看解析
echo PWN > COLLEGE
/challenge/run < PWN
我们使用"<"将文件"PWN"作为输入流

Grepping stored results 对存储结果进行grep

您知道如何运行命令,如何重定向其输出(例如 >),以及如何搜索结果文件(例如 grep)。让我们把这个放在一起吧!

在这个更复杂的关卡时,我们希望您:

  1. /challenge/run 的输出重定向到 /tmp/data.txt
  2. 这将产生 10 万行文本,其中之一是 /tmp/data.txt 中的flag。
  3. 使用grep得到flag!
查看解析
/challenge/run > /tmp/data.txt
grep pwn.college /tmp/data.txt

Grepping live output grep实时输出

事实证明,您可以 “省去中间人”,避免将结果存储到文件中的需要,就像您在上一关中所做的那样。您可以使用 |(管道符)运算符来使用它。管道左侧命令的标准输出将连接到管道右侧的命令的标准输入端(通过管道连接到该管道)。例如:

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 将输出 10 万行文本,包括flag。用 Grep 找出flag!

查看解析
/challenge/run | grep pwn.college

image-20241020183031498

Grepping errors grep错误

您知道如何将错误输出重定向到文件,并且知道如何通过管道将输出传输到另一个程序。但是,如果您想直接 grep 错误输出怎么办?

> 运算符将给定的文件描述符重定向到文件,并且您使用了 2> 重定向 fd 2,这是标准错误。管道符 | 仅将标准输出重定向到另一个程序,并且不存在管道符的 2|形式!它只能重定向标准输出(FD 1)。

shell 有一个 >& 运算符,它将一个文件描述符重定向到另一个文件描述符。这意味着我们可以有一个两步过程来grep修复错误:首先,我们将标准错误重定向到标准输出(2>&1),然后将现在组合的stderr和stdout作为正常管道(|)!

现在就试试吧!与上一关一样,此关卡将用输出为难您,但这次是标准错误输出。解决它并找到flag!

查看解析
/challenge/run 2>&1 | grep pwn.college

image-20241020184751573

Filtering with grep -v 使用 grep -v 进行反向过滤

grep 命令有一个非常实用的选项:-v(反向匹配)。普通的 grep 显示符合模式的文本行,而 grep -v 则显示不符合模式的行:

hacker@dojo:~$ cat data.txt
hello hackers!
hello world!
hacker@dojo:~$ cat data.txt | grep -v world
hello hackers!
hacker@dojo:~$

有时候,要筛选出你真正需要的数据,唯一的方法就是先过滤掉你不想要的数据。在这个挑战中,/challenge/run 会将真实的flag输出到 stdout,但同时也会混杂输出 1000 多个诱饵flag(这些诱饵flag的某个位置包含单词 DECOY)。你需要过滤掉这些诱饵flag,同时保留真实的flag!

使用 grep -v 过滤掉所有包含 "DECOY" 的行,从而揭示出真实的flag!

查看解析
/challenge/run | grep -v DECOY

Filtering with sed 使用 sed 进行过滤

grep 并非模式匹配的唯一方法。有时真实数据和干扰数据会混在同一行中,而我们想要过滤掉这些干扰数据。为此,我们可以使用 sedsed 提供了一种简单的方法来将文本中的模式替换为不同的词。匹配和替换的语法很简单:

sed "s/旧词/新词/g"

其中:

  • s/ - 表示替换(substitute)
  • 旧词 - 要替换的单词
  • 新词 - 替换后的新单词
  • /g - 全局替换(搜索并替换所有匹配的模式)

在这个挑战中,/challenge/run 会输出flag,但在每个字符之间都会插入字符串 "FAKEFLAG"。你的任务是从flag中过滤掉这些干扰数据。祝你好运!

查看解析
/challenge/run | sed "s/FAKEFLAG//g"
使用sed命令将"FAKEFLAG"替换为空

Duplicating piped data with tee 使用tee复制管道数据

当您将数据从一个命令传递到另一个命令时,您当然不会再在屏幕上看到它。这并不总是需要的:例如,您可能希望查看数据在命令之间流动时的数据,以调试意外结果(例如,“为什么第二个命令不起作用???”)。

幸运的是,有一个解决方案!tee 命令以管道中的“T 型分离器”命名,它将流经管道的数据复制到命令行上提供的任意数量的文件中。例如:

hacker@dojo:~$ echo hi | tee pwn college
hi
hacker@dojo:~$ cat pwn
hi
hacker@dojo:~$ cat college
hi
hacker@dojo:~$

如您所见,通过向 tee 提供两个文件,我们最终得到了三个管道传入数据的副本:一个到 stdout,一个到 pwn 文件,一个到 college 文件。您可以想象如何使用它来调试事情变得混乱:

hacker@dojo:~$ command_1 | command_2
Command 2 failed!
hacker@dojo:~$ command_1 | tee cmd1_output | command_2
Command 2 failed!
hacker@dojo:~$ cat cmd1_output
Command 1 failed: must pass --succeed!
hacker@dojo:~$ command_1 --succeed | command_2
Commands succeeded!

image-20241020190021882

现在,你试试吧!在这关中 /challenge/pwn 必须通过管道传输到 /challenge/college,但你需要拦截数据才能看到 pwn 需要你做什么!

查看解析
/challenge/pwn | tee output | /challenge/college
cat output
通过使用"tee"将"pwn"程序的输出流复制到"output"

Process substitution for input 输入进程替换

有时你需要比较两个命令的输出,而不是两个文件。你可能会想先将每个输出保存到文件:

hacker@dojo:~$ command1 > file1
hacker@dojo:~$ command2 > file2
hacker@dojo:~$ diff file1 file2

但有一个更优雅的方法!Linux 遵循“一切皆文件”的哲学。也就是说,系统努力为大多数资源提供类似文件的访问方式,包括运行程序的输入和输出!Shell 遵循这一哲学,允许你使用任何接受文件参数的命令行工具,并将其连接到程序的输出,正如你在前几个关卡中学到的那样。

有趣的是,我们可以更进一步,将程序的输入和输出连接到命令的参数。这通过进程替换来实现。对于从命令读取(输入进程替换),使用 <(command)。当你写入 <(command) 时,bash 会运行该命令并将其输出连接到一个它创建的临时文件。这当然不是一个真正的文件,它被称为命名管道,因为它有一个文件名:

hacker@dojo:~$ echo <(echo hi)
/dev/fd/63
hacker@dojo:~$

/dev/fd/63 是从哪里来的?bash 用连接到命令输出的命名管道文件的路径替换了 <(echo hi)!当命令运行时,从这个文件读取数据将会从命令的标准输出读取数据。通常,这通过接受输入文件作为参数的命令来完成:

hacker@dojo:~$ cat <(echo hi)
hi
hacker@dojo:~$

当然,你可以多次指定:

hacker@dojo:~$ echo <(echo pwn) <(echo college)
/dev/fd/63 /dev/fd/64
hacker@dojo:~$ cat <(echo pwn) <(echo college)
pwn
college
hacker@dojo:~$

现在轮到你的挑战了!回忆你在“理解命令”中的 diff 挑战中学到的内容。在那个挑战中,你比较了两个文件。现在,你将比较两个命令的输出:/challenge/print_decoys 会打印一堆诱饵flag,而 /challenge/print_decoys_and_flag 会打印相同的诱饵flag加上真实flag。

使用进程替换与 diff 来比较这两个程序的输出,找到你的flag!

查看解析
diff <(/challenge/print_decoys) <(/challenge/print_decoys_and_flag)

Writing to multiple programs 写入多个程序

好的,现在我们明白了我们可以用 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 在其手册页中所说,它被设计为写入文件和标准输出:

TEE(1)                           User Commands                          TEE(1)

NAME
       tee - read from standard input and write to standard output and files

Luckily, Linux follows the philosophy that "everything is a file". This is, the system strives to provide file-like access to most resources, including the input and output of running programs! The shell follows this philosophy, allowing you to, for example, use any utility that takes file arguments on the command line (such as tee) and hook it up to the input or output side of a program!
幸运的是,Linux 遵循“一切都是文件”的理念。 也就是说,系统努力提供对大多数资源的类似文件的访问,包括正在运行的程序的输入和输出! shell 遵循这一理念,例如,允许您使用任何在命令行上获取文件参数的实用程序(例如 tee)并将其挂接到程序的输入端或输出端!

这是使用所谓的 进程替换 完成的。 如果你写一个 >(rev) 的参数,bash 将运行 rev 命令(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

在上面,发生了以下事件序列:

  1. bash 启动了 rev 命令,将命名管道(可能是 /dev/fd/63,我们可以使用命令echo >(rev)看看命名管道)挂接到 rev 的标准输入
  2. bash 启动了 tee 命令,将管道挂接到其标准输入,并将 tee 的第一个参数替换为 /dev/fd/63tee 甚至从未看到过参数 >(rev);Shell 在推出 tee之前替换了
  3. bash 使用 echo 内置函数将 HACK 打印到 tee 的标准输入中
  4. tee 读取 HACK,将其写入标准输出,然后将其写入 /dev/fd/63(连接到 rev 的 stdin管道)
  5. rev 从其标准输入中读取 HACK,将其反转,并将 KCAH 写入标准输出

image-20241020195346858

个人理解是将输出流写入文件是直接使用>,而使用>()是将输出流导到下一个程序输入流的管道中

stdin 是标准输入流(Standard Input)的缩写,通常用于程序接收输入数据。

现在轮到你了! 在这个挑战中,我们有 /challenge/hack/challenge/the/challenge/planet。 运行 /challenge/hack 命令,并将其输出复制为 /challenge/the/challenge/planet 命令的输入!


补充!

细心的学习者会意识到以下内容是等效的:

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) >(/challenge/planet)
通过使用"tee"将"pwn"程序的输出流复制到"output"

Split-piping stderr and stdout 拆分管道stderr和stdout

现在,让我们把你的知识放在一起。您必须掌握最终的管道任务:将 stdout 重定向到一个程序,将 stderr 重定向到另一个程序。

当然,这里的挑战在于 | 运算符将左侧命令的 stdout 与右侧命令的 stdin 链接起来。当然,您已经使用 2>&1 将 stderr 重定向到 stdout,因此,管道 stderr 重叠,但这会混合 stderr 和 stdout。如何保持不混合?

您需要结合您对 >(),2>,|的知识和理解,完成我给你留下的任务。

在本次挑战中,给出以下提示:

  • /challenge/hack:该程序会在 stdoutstderr 上生成数据
  • /challenge/the: 您必须将 Hackstderr 重定向到此程序
  • /challenge/planet: 您必须将 Hackstdout 重定向到此程序

Go get the flag! 去拿flag吧!

查看解析
/challenge/hack > >(/challenge/planet) 2> >(/challenge/the)
这里试着解释一下为什么
/challenge/hack >(/challenge/planet) 2>(/challenge/the)
不行
使用 `>(command)` 时,它会创建一个可用于重定向的文件描述符,而不是用作参数
`>`用于提取出输出流,`>()`用于指定程序输入流的管道

image-20241020204553520

Named pipes 命名管道

你已经学习了使用 | 的管道,也看到了进程替换会创建临时的命名管道(如 /dev/fd/63)。你还可以创建持久存在于文件系统中的命名管道!这些被称为 FIFO,即先进先出。

你可以使用 mkfifo 命令创建 FIFO:

hacker@dojo:~$ mkfifo my_pipe
hacker@dojo:~$ ls -l my_pipe
prw-r--r-- 1 hacker hacker 0 Jan 1 12:00 my_pipe
-rw-r--r-- 1 hacker hacker 0 Jan 1 12:00 some_file
hacker@dojo:~$

注意权限开头的 p - 这表明它是一个管道!这与普通文件开头的 - 明显不同,例如上面例子中的 some_file

与进程替换创建的自动命名管道不同:

  • 你可以控制 FIFO 的创建位置
  • 它们会一直存在直到你删除它们
  • 任何进程都可以通过路径向它们写入(例如 echo hi > my_pipe
  • 你可以用 ls 看到它们,并像文件一样检查它们

FIFO 的一个问题是,它们会"阻塞"任何对其的操作,直到管道的读取端和写入端都准备就绪。例如:

hacker@dojo:~$ mkfifo myfifo
hacker@dojo:~$ echo pwn > myfifo

为了执行 echo pwn > myfifo,bash 会以写入模式打开 myfifo 文件。但是,这个操作会挂起,直到有东西也以读取模式打开该文件(从而完成管道连接)。这可以在另一个控制台中完成:

hacker@dojo:~$ cat myfifo
pwn
hacker@dojo:~$

这里发生了什么?当我们运行 cat myfifo 时,管道的两端连接都已建立并解除阻塞,允许 echo pwn > myfifo 运行,它将 pwn 发送到管道中,然后被 cat 读取。

当然,普通文件也可以完成类似的操作:你已经学会了如何将内容写入文件并用 cat 读取。为什么要使用 FIFO 呢?以下是关键区别:

  • 无磁盘存储:FIFO 直接在进程间内存中传递数据 - 不会保存到磁盘
  • 临时数据:一旦数据从 FIFO 中读取,它就消失了(不像文件中的数据会持久保存)
  • 自动同步:写入者会阻塞直到读取者准备好,反之亦然。这实际上很有用!它提供了自动同步。考虑上面的例子:使用 FIFO,无论是 cat myfifo 还是 echo pwn > myfifo 先执行都没关系;每个都会等待另一个。而对于文件,你需要确保在读取者之前执行写入者。
  • 复杂数据流:FIFO 有助于促进复杂的数据流,以灵活的方式合并和拆分数据等。例如,FIFO 支持多个读取者和写入者。

这个挑战将是对 FIFO 的简单介绍。你需要创建一个 /tmp/flag_fifo 文件,并将 /challenge/run 的标准输出重定向到它。如果成功,/challenge/run 会将标志写入 FIFO!去完成它吧!

提示:FIFO 的阻塞行为使得在单个终端中解决这个挑战很困难。你可能需要为此挑战使用桌面或 VSCode 模式,以便可以启动两个终端。

查看解析
mkfifo /tmp/flag_fifo
/challenge/run > /tmp/flag_fifo
不中断进程,打开另一个终端
cat /tmp/flag_fifo

/challenge/run > /tmp/flag_fifo输出:

你已成功将 /challenge/run 的输出重定向到位于 /tmp/flag_fifo 的 FIFO!
Bash 现在会尝试以写入模式打开该 FIFO,将其作为 /challenge/run 的标准输出。请记住,对 FIFO 的操作将会阻塞,直到读取端和写入端都准备就绪,因此在你开始从该 FIFO 读取数据之前,/challenge/run 实际上并不会启动执行!

Shell Variables Shell 变量

Printing Variables 打印变量

让我们从打印变量开始。这次/challenge/run 程序不会也不能给你flag,但这没关系,因为这个flag已经被放到名为 “FLAG” 的变量中了!就让你的shell打印出来吧!

您可以使用多种方法完成此操作,但我们将从 echo 开始。此命令仅打印内容。例如:

hacker@dojo:~$ echo Hello Hackers!
Hello Hackers!

你也可以通过在变量名称前加上 $ 来打印出带有 echo 的变量。例如,有一个变量 PWD,它始终保存当前 shell 的当前工作目录。您可以按如下方式打印出来:

hacker@dojo:~$ echo $PWD
/home/hacker

现在轮到你了。让您的 shell 打印出 FLAG 变量并解决此挑战!

查看解析
echo $FLAG
我们通过"$"符来指定变量

Setting Variables 设置变量

当然,除了读取存储在变量中的值外,您还可以将值写入变量。与许多其他语言一样,这是使用 = 完成的。要将变量 VAR 设置为值 1337,您可以使用:

hacker@dojo:~$ VAR=1337

请注意=的使用!如果你加上空格(例如,VAR = 1337),shell 将无法识别变量赋值,而是尝试运行 VAR 命令(不存在)

还要注意,这使用 VAR 而不是 $VAR$ 仅用于访问变量。在 shell 术语中,这个 $ 的前缀会触发所谓的变量扩展,并且令人惊讶地,它是许多潜在漏洞的来源(如果您对此感兴趣,请在熟悉命令行后查看 Art of the Shell 道场(pwn.college)!

设置变量后,您可以使用之前学到的技术访问它们,例如:

hacker@dojo:~$ echo $VAR
1337

要解决此关卡问题,必须将 PWN 变量设置为值 COLLEGE。注意:变量的名称和值都区分大小写!PWNpwn 不同,COLLEGECollege 不同

查看解析
PWN=COLLEGE
我们通过"="符来设置变量

Multi-word Variables 多字变量

在这个关卡,您将学习报价。空格在 shell 中具有特殊意义,有些地方你不能随意使用它们。回想一下我们的变量设置:

hacker@dojo:~$ VAR=1337

这会将 VAR 变量设置为 1337,但如果要将其设置为 1337 SAUCE,该怎么办?您可以尝试以下操作:

hacker@dojo:~$ VAR=1337 SAUCE

这看起来很合理,但它不起作用,原因与需要在 = 周围没有空格的原因类似。当 shell 看到空格时,它会结束变量赋值并将下一个单词(在本例中为 SAUCE)解释为命令。要将 VAR 设置为 1337 SAUCE,您需要引用它:

hacker@dojo:~$ VAR="1337 SAUCE"

在这里,shell 将 1337 SAUCE 读取为单个标记,并愉快地将该值设置为 VAR。在此关卡中,您需要将变量 PWN 设置为 COLLEGE YEAH。祝你好运!

查看解析
PWN="COLLEGE YEAH"

Exporting Variables 导出变量

默认情况下,您在 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

在此挑战中,您调用 /challenge/run前,要将 PWN 变量导出并设置为值 COLLEGE,并将 COLLEGE 变量设置为值 PWN 但不导出(不被 /challenge/run 继承)。祝你好运!

查看解析
PWN=COLLEGE
COLLEGE=PWN
export PWN
/challenge/run
我们使用"export"命令导出变量

Printing Exported Variables 打印导出的变量

有多种方法可以访问 bash 中的变量。Echo 只是其中之一,我们现在将在这次挑战中至少再学习一个。

试试 env 命令:它会打印出 shell 中设置的每个导出变量,你可以查看该输出以找到 FLAG 变量!

查看解析
env | grep FLAG
我们使用"env"命令查看变量

Storing Command Outpt 存储命令输出

在使用 shell 的过程中,您经常希望将某些命令的输出存储到变量中。幸运的是,shell 使用称为 Command Substitution 的东西使这变得非常容易!观察:

hacker@dojo:~$ FLAG=$(cat /flag)
hacker@dojo:~$ echo "$FLAG"
pwn.college{blahblahblah}
hacker@dojo:~$

现在是你的练习时刻。将 /challenge/run 命令的输出直接读取到名为 PWN 的变量中,它将包含flag!


补充:你也可以用反引号代替 $()在上面的例子中,

FLAG=`cat /flag` 

而不是 FLAG=$(cat /flag)。这是一种较旧的格式,并且有一些缺点(例如,假设您想要嵌套命令替换)。你会怎么用? $(cat $(find / -name flag))吗?pwn.college 的官方立场是你应该使用 $(blah) 而不是

`blah`
查看解析
PWN=$(/challenge/run)
cat $PWN

Reading Input 读取输入

我们将从读取用户 (你) 的输入开始。这是使用恰如其分地命名为 read builtin 完成的,它读取 input!

下面是一个使用 -p 参数的示例,它允许您指定一个提示符(否则,现在阅读本文时,您将很难在下面的示例中将输入与输出分开):

hacker@dojo:~$ read -p "INPUT: " MY_VARIABLE
INPUT: Hello!
hacker@dojo:~$ echo "You entered: $MY_VARIABLE"
You entered: Hello!

请记住,read会从您的标准输入中读取数据!上面的第一个 Hello!input 而不是 output。让我们试着更明确地说明这一点。在这里,我们对每行的开头进行了注释,该行是表示用户的 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!

在这个挑战中,你的工作是使用 readPWN 变量设置为值 COLLEGE。祝你好运!

查看解析
read PWN
COLLEGE
我们使用"read"指令来输入设置变量

Reading Files 读入文件

通常,当 shell 用户想要将文件读入环境变量时,他们会执行以下操作:

hacker@dojo:~$ echo "test" > some_file
hacker@dojo:~$ VAR=$(cat some_file)
hacker@dojo:~$ echo $VAR
test

这很有效,但它代表了牢骚满腹的黑客所说的cat命令的无效使用。也就是说,运行一个完全不同的程序来读取文件是一种浪费。事实证明,你可以直接使用 shell 的力量!

之前,您将用户输入read到一个变量中(将用户输入读入一个变量)。您之前还将文件重定向到命令输入!将它们放在一起,您就可以使用 shell 读取文件。

hacker@dojo:~$ echo "test" > some_file
hacker@dojo:~$ read VAR < some_file
hacker@dojo:~$ echo $VAR
test

上面发生了什么?该示例将 some_file重定向到 read 的标准输入,因此当 read 读入 VAR 时,它会从文件中读取!

现在,使用上述方法将 /challenge/read_me 读取到 PWN 环境变量中,我们将为您提供flag!/challenge/read_me 会不断变化,因此您需要使用一个命令将其直接读取到 PWN 变量中!

查看解析
read PWN < /challenge/read_me

Data Manipulation 数据操作

Translating characters 字符转换

管道数据的目的之一是修改数据。许多 Linux 命令能以非常酷的方式帮助你修改数据。其中一个就是 tr,它能换从标准输入接收的字符,并将结果输出到标准输出。

在最基本的用法中,tr 将其第一个参数中的字符转换为第二个参数中的字符:

hacker@dojo:~$ echo OWN | tr O P
PWN
hacker@dojo:~$

它还可以处理多个字符,将第一个参数中不同位置的字符替换为第二个参数中对应位置的字符:

hacker@dojo:~$ echo PWM.COLLAGE | tr MA NE
PWN.COLLEGE
hacker@dojo:~$

现在轮到你了!在这个关卡中,/challenge/run 会输出flag,但会交换所有字符的大小写(例如,A 会变成 a,反之亦然)。你能用 tr 撤销这个转换并获取flag吗?

查看解析
/challenge/run | tr 'a-zA-Z' 'A-Za-z'
使用"tr"命令将字母大小写转换

Deleting characters 删除字符

tr 也可以将字符转换为空(即删除它们)。这是通过 -d flag和一个指定要删除哪些字符的参数来实现的:

hacker@dojo:~$ echo PAWN | tr -d A
PWN
hacker@dojo:~$

很简单!现在你来试试。我会在flag字符之间插入一些干扰字符(具体是:^%)。使用 tr -d 来删除它们!

查看解析
/challenge/run | tr -d ^%
使用"tr -d"命令删除干扰字符

删除换行符

一类常见的需要删除的字符是行分隔符。当你有数据流想要转换成单行以便进一步处理时,就会遇到这种情况。你可以通过转义来指定换行符,就像处理其他字符一样:

hacker@dojo:~$ echo "hello_world!" | tr _ "\n"
hello
world!
hacker@dojo:~$

这里,反斜杠(\)表示跟在它后面的字符代表一个难以直接在 Shell 中输入的字符的替代符。当然,换行符很难输入,因为通常你按下 Enter 键就会直接运行命令。\n 就是这个换行符的替代符,它必须放在引号内,以防止 Shell 解释器本身尝试解释它,而是将其传递给 tr

现在,让我们将其与删除功能结合起来。在这个挑战中,我们会在flag中注入大量换行符。使用 tr-d flag和转义的换行符说明来删除它们!


有趣的事实! 想要真正替换一个反斜杠(\)字符?因为 \ 是转义字符,所以你需要对它进行转义!\\ 会被 tr 视为一个反斜杠。虽然这与本挑战无关,但依然是个有趣的事实!

查看解析
/challenge/run | tr -d "\n"
使用"tr -d"命令删除干扰字符

使用 head 提取前几行

在你的 Linux 旅程中,会遇到需要从输出非常冗长的程序中仅获取开头部分内容的情况。这时,你就会用到 headhead 命令用于显示其输入的前几行:

hacker@dojo:~$ cat /something/very/long | head
this
is
just
the
first
ten
lines
of
the
file
hacker@dojo:~$

默认情况下,它显示前 10 行,但你可以使用 -n 选项来控制这个数量:

hacker@dojo:~$ cat /something/very/long | head -n 2
this
is
hacker@dojo:~$

这个挑战的 /challenge/pwn 会输出大量数据,你需要通过管道将其传递给 head 以仅获取前 7 行,然后将这些行继续通过管道传递给 /challenge/college。如果你操作正确,它将给你flag!你的解决方案将是一个包含两个管道连接三个命令的复合命令。祝你好运!

查看解析
/challenge/pwn | head -n 7 | /challenge/college
使用"head -n"命令提取文本前几行

提取特定文本部分

有时,你想要提取特定的数据列,例如第一列、第三列或第 42 列。为此,可以使用 cut 命令。

例如,假设你有以下数据文件:

hacker@dojo:~$ cat scores.txt
hacker 78 99 67
root 92 43 89
hacker@dojo:~$

你可以使用 cut 来提取特定的

hacker@dojo:~$ cut -d " " -f 1 scores.txt
hacker
root
hacker@dojo:~$ cut -d " " -f 2 scores.txt
78
92
hacker@dojo:~$ cut -d " " -f 3 scores.txt
99
43
hacker@dojo:~$

-d 参数指定列分隔符(列是如何分隔的)。在这个例子中,是空格字符。当然,这里必须用引号括起来,这样 Shell 才知道这个空格是一个参数,而不是分隔其他参数的空格!-f 参数指定字段编号(要提取哪一列)。

在这个挑战中,/challenge/run 程序会给你很多行包含随机数字和单个字符(flag的字符)作为列的数据。使用 cut 提取flag字符,然后通过管道将它们传递给 tr -d "\n"(像上一关那样!)将它们连接成一行。你的解决方案将类似于 /challenge/run | cut ??? | tr ???,其中 ??? 需要你填写。

查看解析
/challenge/run | cut -d " " -f 2 | tr -d "\n"
使用"cut -d"命令指定通过空格来分隔各个列,使用"cut -f"命令指定要提取的列

数据排序

文件(或命令的输出行)并不总是你需要的顺序!sort 命令可以帮助你整理数据。它从输入(或文件)读取行,并按排序后的顺序输出:

hacker@dojo:~$ cat names.txt
  hack
  the
  planet
  with
  pwn
  college
hacker@dojo:~$ sort names.txt
  college
  hack
  planet
  pwn
  the
  with
hacker@dojo:~$

默认情况下,sort 按字母顺序排列行。参数可以改变这一点:

  • -r:逆序(从 Z 到 A)
  • -n:数字排序(针对数字)
  • -u:仅显示唯一行(去除重复项)
  • -R:随机顺序!

在这个挑战中,/challenge/flags.txt 文件中包含 100 个假flag,其中混有一个真实flag。当按字母顺序排序时,真实flag会出现在末尾(我们在生成假flag时确保了这一点)。去获取它吧!

查看解析
sort /challenge/flags.txt
使用"sort"命令按字母顺序排序

Processes and Jobs 进程和作业

Listing Prcocess 列出进程

首先,我们将学习使用 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 时间总量(因为这些进程的CPU消耗需求非常低,它们甚至还没有消耗 1 秒!)

在大多数情况下,这就是您在默认 ps 中看到的全部内容。为了使其有用,我们需要传递一些参数。

由于 ps 是一个非常古老的实用程序,因此它的使用有点混乱。有两种方法可以指定参数。

“标准”语法:在此语法中,您可以使用 -e 列出“每个”进程,使用 -f 列出“完整格式”输出,包括参数。这些可以组合成一个参数 -ef

“BSD” 语法:在此语法中,您可以使用 a 列出所有用户的进程,使用 x 列出未在终端中运行的进程,使用 u 表示“用户可读”输出。这些可以组合成一个参数 aux

ps -efps aux 这两种方法产生的输出略有不同,但可以交叉识别。

让我们在 dojo虚拟机 中尝试一下:

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 命令。这与 ps aux 基本相同:

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 -efps aux 之间有许多共同点:都显示用户(USER 列)、PID、TTY、进程的开始时间 (STIME/START)、总利用的 CPU 时间 (TIME) 和命令 (CMD/COMMAND)。ps -ef 还输出父进程 IDPPID),就是与进程启动相关的进程的 PID,而 ps aux 输出进程正在使用的系统 CPU 和内存总数的百分比。另外,还有许多其他内容我们现在不会讨论。

image-20241020221511143

无论如何!让我们练习一下。在这个关卡中,我再次将 /challenge/run 重命名为随机文件名,这一次你不能ls /challenge 目录!但是我也启动了它,所以可以在正在运行的进程列表中找到它,找出文件名,然后直接为flag重新启动它!祝你好运!

注意:ps -efps aux 都将命令列表截断到终端的宽度(这就是为什么上面的示例在屏幕右侧排列得如此漂亮。如果您无法读取进程的整个路径,则可能需要放大终端(或将输出重定向到某个位置以避免这种截断行为)!

查看解析
ps -ef
使用"ps -ef"命令查看正在运行的进程,其中包含了/challenge/run的程序名< /challenge/read_me

Killing Processes kill进程

您已经启动了流程,查看了流程,现在您将学习如何终止流程!在 Linux 中,这是使用名称激进的 kill 命令完成的。使用默认选项(这就是我们将在本关卡中介绍的全部内容),kill 将终止一个进程,使其有机会在不再存在之前将其事务整理好。

假设您在另一个终端中启动了一个讨厌的 sleep 进程(sleep 是一个程序,它只是在命令行上指定的秒数内挂起,在本例中为 1337 秒),如下所示:

hacker@dojo:~$ sleep 1337

我们如何摆脱它?您可以通过将进程标识符(来自 psPID)作为参数传递来使用 kill 来终止它,如下所示:

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:~$

现在,是时候终止您的第一个进程了!在这个挑战中,/challenge/run 将拒绝在 /challenge/dont_run 运行时运行!您必须找到dont_run进程并终止它。如果你失败了,pwn.college 将不认为你理解了知识点。祝你好运。

查看解析
使用"ps -ef"命令查看正在运行的"dont_run"程序的PID,然后使用"kill"命令终止"dont_run"进程< /challenge/read_me

Interrupting Processes 中断进程

您已经学会了如何使用 kill 命令杀死其他进程,但有时您只想摆脱阻塞终端的进程!幸运的是,终端有一个热键:Ctrl-C(例如,按住 Ctrl 键并按 C)向正在等待终端输入的任何应用程序发送“中断”,通常,这会导致应用程序干净地退出。

在这里试试吧!/challenge/run 将拒绝给你flag,直到你中断它。祝你好运!


对于非常感兴趣的人,请查看这篇关于终端和“控制代码”(例如 Ctrl^C)的文章。

查看解析
/challenge/run
Ctrl+c

Killing Misbehaving Processes 终止异常进程

有时,行为异常的进程可能会干扰你的工作。这些进程可能需要被终止...

在这个挑战中,有一个诱饵进程正在占用关键资源 - 一个位于 /tmp/flag_fifo 的命名管道(FIFO),就像在管道练习的 FIFO 挑战中一样,/challenge/run 希望向这个管道写入你的标志。你需要 kill 这个进程。

你的大致工作流程应该是:

  1. 检查正在运行的进程。
  2. 在列表中找到 /challenge/decoy 并确定其进程 ID。
  3. 使用 kill 终止它。
  4. 运行 /challenge/run 来获取flag,而不会被诱饵淹没(你不需要重定向其输出;它会自动写入 FIFO)。

祝你好运!


注意: 即使在杀死诱饵进程后,你可能仍会看到一些诱饵标志出现。这是因为 Linux 管道是缓冲的:从概念上讲,它们有一种数据流动的长度,你可能在数据还在管道中时杀死了诱饵进程。这些已经进入管道的数据将继续流向另一端(你的 cat)。如果你等待一秒钟,你会看到诱饵停止出现,然后你可以运行

查看解析
ps -e | grep decoy
kill 144
/challenge/run
不中断进程,打开另一个终端
cat /tmp/flag_fifo
真flag在最底下

Suspending Process 暂停进程

您已经学会了使用 Ctrl-C 中断进程,但您可以使用不那么激烈的措施来恢复您的终端!您可以使用 Ctrl-Z 将进程挂起到后台。在这个关卡中,我们将探索它是如何工作的,在下一个关卡中,我们将弄清楚如何恢复那些暂停的进程!

查看解析
/challenge/run
Ctrl+z
/challenge/run

Resuming Process 恢复进程

通常,当您暂停进程时,您需要在某个时候恢复它们。否则,为什么不直接终止他们呢?为了恢复进程,您的 shell 提供了 fg 命令,这是一个内置命令,用于获取暂停的进程,恢复它,并将其放回终端的前台。

快来试试吧!此挑战的run进程需要您暂停它,然后恢复它。祝你好运!

查看解析
/challenge/run
Ctrl+z
fg

Backgrounding Processes 后台进程

您已使用 fg 命令在前台恢复了进程。您还可以使用 bg 命令在后台恢复进程!这将允许进程继续运行,同时将 shell 交还给你以调用更多命令。

此关卡的run希望看到自己的另一个副本正在运行,而不是暂停,并且使用相同的终端。如何?使用终端启动它,然后暂停它,然后使用 bg 将其置于后台,并在第一个副本在后台运行时启动另一个副本!


提高:如果您对一些更深入的细节感兴趣,请查看如何查看暂停和后台属性之间的差异!请允许我演示一下。首先,让我们暂停sleep

hacker@dojo:~$ sleep 1337
^Z
[1]+  Stopped                 sleep 1337
hacker@dojo:~$

sleep进程现在在后台暂停。我们可以通过使用 -o 选项启用 stat 列输出来通过 ps 看到这一点:

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,进程已暂停。bashSTAT 列中的 S 表示 bash 在等待输入时处于休眠状态。ps 列中的 R 表示它正在积极运行,+ 表示它位于前台!

观察当我们在后台恢复sleep进程时会发生什么:

hacker@dojo:~$ bg
[1]+ sleep 1337 &
hacker@dojo:~$ ps -o user,pid,stat,cmd
USER         PID STAT CMD
hacker       702 Ss   bash
hacker       762 S    sleep 1337
hacker      1224 R+   ps -o user,pid,stat,cmd
hacker@dojo:~$

BOOM!sleep现在有一个 S。它在运行的时候,就是在睡觉而已,但它并没有暂停!它也在后台运行,因此没有 +

查看解析
/challenge/run
Ctrl+z
bg
/challenge/run

Foregrounding Processes 前台进程

假设您有一个后台进程,并且您希望对它进行更多操作。你是做什么工作的?好吧,您可以使用 fg 将后台进程置于前台,就像将暂停的进程置于前台一样!这个关卡将引导您完成这个任务!

查看解析
/challenge/run
Ctrl+z
bg
fg

Starting Backgrounded Processes 后台启动进程

当然,您不必暂停进程来使它们成为后台:您可以立即后台启动进程!这很容易;您只需在命令后附加一个&,就像这样:

hacker@dojo:~$ sleep 1337 &
[1] 1771
hacker@dojo:~$ ps -o user,pid,stat,cmd
USER         PID STAT CMD
hacker      1709 Ss   bash
hacker      1771 S    sleep 1337
hacker      1782 R+   ps -o user,pid,stat,cmd
hacker@dojo:~$ 

在这里,sleep 在后台主动运行,而没有暂停。现在轮到你练习了!在的后台启动 /challenge/run进程!

查看解析
/challenge/run &

Process Exit Codes 处理退出代码

每个 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 返回的退出代码,然后使用该错误代码作为参数运行 /challenge/submit-code。祝你好运!

查看解析
/challenge/get-code
echo $?

Perceiving Permissions 感知权限

Changing File Ownership 更改文件所有权

首先我们要理解的概念是:文件所有权。

Linux 中的每个文件都由系统上的用户拥有。大多数情况下,在日常生活中,该用户是我们每天登录的用户。

在共享系统上(例如在计算机实验室中),可能有许多人使用不同的用户帐户,在他们自己的主目录中都有自己的文件。但是,即使在非共享系统(例如您的个人PC)上,Linux仍然有许多用于不同任务的“服务”用户帐户。

两个最重要的用户帐户是:

  1. 您的用户帐户!在 pwn.college 上,无论你的用户名是什么,这都是hacker用户。
  2. root。这是 admin 账户,在大多数安全情况下,这是最终的奖品。如果您接管了 root 用户,您几乎可以肯定已经实现了您的黑客任务!(得到了linux系统主机的最高用户权限

就算如此对我们有什么影响?嗯,事实证明,我们阻止你执行 cat /flag 的方法是让 root 用户拥有 /flag,配置它的权限,这样其他用户就不能读取它(你稍后会学习如何配置权限),并将实际的质询配置为以 root 用户身份运行(你稍后也会学习如何这样做)。结果是,当你执行 cat /flag 时,你会得到:

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:~#

可以看见我们提示符末尾由$变成了#

有趣的是,我们可以更改文件的所有权!这是通过 chownchange owner) 命令完成的:

chown [username] [file]

通常,chown 只能由 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:~#

college_file 的所有者已更改为hacker用户,以及 hacker 如何对它执行任何 root 操作!如果这是 /flag 文件,则意味着hacker用户将能够读取它!

在这个关卡中,我们将练习将 /flag 文件的所有者更改为 hacker 用户,然后是 flag。仅对于这个挑战,hacker用户拥有使用chown命令的权限,这样您就可以作为hacker用户尽情地使用 chown(要记住,通常这需要您是 root用户)。明智地使用这种力量,然后得到flag!

查看解析
chown hacker /flag
cat /flag
使用"chown"更改"/flag"文件的所有者用户

Group and Files 用户组和文件

分享就是关怀,而分享是 Linux 设计中内置的。文件拥有权同时属于用户。一个组中可以有多个用户,并且一个用户可以是多个组的成员。

您可以使用 id 命令检查您所属的组:

hacker@dojo:~$ id
uid=1000(hacker) gid=1000(hacker) groups=1000(hacker)
hacker@dojo:~$

这里,hacker 用户hacker 组中。

组最常见的用例是控制对不同系统资源的访问。例如,pwn.college 中的 “Practice Mode”(练习模式) 授予你 root 权限,以便更好地进行调试,等等。这是通过在 "Practice Mode" 虚拟机启动时为您提供一个额外的组来处理的:

hacker@dojo:~$ id
uid=1000(hacker) gid=1000(hacker) groups=1000(hacker),27(sudo)
hacker@dojo:~$

在"Practice Mode"中我们在"sudo"用户组,在这个用户组中的用户可以使用sudo命令,即以root用户的身份运行程序

经典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 组的成员。

字符设备是计算机系统中的一种设备类型,主要用于处理以字符为单位的输入和输出

不过,dojo 中的 /flag 文件就没有这样的运气了!请思考以下示例:

hacker@dojo:~$ id
uid=1000(hacker) gid=1000(hacker) groups=1000(hacker)
hacker@dojo:~$ ls -l /flag
-r--r----- 1 root root 53 Jul  4 04:47 /flag
hacker@dojo:~$ cat /flag
cat: /flag: Permission denied
hacker@dojo:~$

这里,flag 文件归 root 用户和 root 组所有,hacker 用户既不是 root 用户,也不是 root 组的成员,因此无法访问该文件。幸运的是,可以使用 chgrpchange group) 命令更改组所有权!除非你对文件有写入权限新组的成员身份,否则这通常需要 root 访问权限,所以让我们以 root 身份检查一下:

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:~#

在这个关卡中,我使拥有它的任何组都可以读取该flag,但该组目前是 root。幸运的是,我还允许您以hacker用户身份调用 chgrp!更改flag文件的组所有权,然后读取flag!

查看解析
chgrp hacker /flag
cat /flag
使用"chgrp"更改"/flag"文件的所有组

Fun With Group Names 用户组名中的乐趣

在前面的关卡中,您可能已经注意到您的hacker用户是hacker组的成员,而 zarduszardus 组的成员。Linux 中有一个约定,每个用户都有自己的组,但并非必须如此。例如,许多计算机实验室会将其所有用户放入一个共享users组中。

重点是,您之前使用的都是 hacker用户组,但在这个关卡中,这行不通。我仍然允许您使用 chgrp,但我已随机化了您的用户所在的组的名称。您需要使用 id 命令来计算出该名称,然后 chgrp 过关!

查看解析
id
使用"id"查看"hacker"用户当前处在的用户组

Changing Permissiongs 更改权限

所以现在我们已经精通所有权的概念了。 现在让我们谈谈另一个概念:文件权限。 回想一下我们的例子:

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:~$

提醒一下,-rw-r--r--中第一个字符是文件类型。 接下来的 9 个字符是文件或目录的实际访问权限, 3 个字符表示拥有用户的权限,3 个字符表示拥有组的权限,3 个字符表示所有其他访问权限(例如,其他用户和其他组)对文件的权限。

这三个字符中的每个字符都表示不同类型的权限:

r -用户/组/其他可以读取文件(或列出目录)
w -用户/组/其他可以修改文件(或创建/删除目录中的文件)
x -用户/组/其他可以作为程序执行文件(或者可以进入目录,例如,使用‘ cd ’)
- -没权限

对于上述college_file,rw-r--r-- 权限条目解码为:``

  • r:拥有该文件的用户(用户 hacker)可以读取该文件
  • w:拥有该文件的用户(用户 hacker)可以写入该文件
  • -:拥有该文件的用户(用户 hacker无法执行该文件
  • r:拥有该文件的组(Hacker 组)中的用户可以读取该文件
  • -:拥有该文件的组中的用户 (group hacker无法写入该文件
  • -:拥有该文件的组中的用户(hacker 组无法执行该文件
  • r:所有其他用户都可以读取
  • -:所有其他用户都无法写入它
  • -:所有其他用户都无法执行它

现在,让我们看看 /flag 的默认权限:

hacker@dojo:~$ ls -l /flag
-r-------- 1 root root 53 Jul  4 04:47 /flag
hacker@dojo:~$

这里,只设置了一个位:拥有用户(在本例中为 root)的 read 权限。 拥有组 (root组) 的成员和所有其他用户无权访问该文件。

您可能想知道,如果没有对文件的组访问权限,chgrp 是如何工作的。 对于这些,我以不同的方式设置权限:

hacker@dojo:~$ ls -l /flag
-r--r----- 1 root root 53 Jul  4 04:47 /flag
hacker@dojo:~$

root小组可以访问! 这就是为什么 chgrp文件使您能够读取该文件的原因。

无论如何! 与所有权一样,文件权限也可以更改。 这是通过 chmodchange mode) 命令完成的。 chmod 的基本用法是:

chmod [OPTIONS] MODE FILE

您可以通过两种方式指定 MODE:作为现有权限模式的修改,或作为覆盖旧模式的全新模式。

在本关卡中,我们将介绍前者:修改现有模式。

chmod 允许您使用 WHO+/-WHAT 的模式格式调整权限,其中 WHO 是 user/group/other,WHAT 是 read/write/execute。

例如,要为拥有用户添加读取访问权限,您可以指定 u+r 模式。 GRoup 和 OTher(或 All 模式)的 WRite 和 Execute 访问权限以相同的方式指定。 更多示例:

  • u+r 为用户的权限添加了 Read 访问权限
  • g+wx 为组的权限添加了 Write 和 Execute 访问权限
  • o-w删除其他用户的 Write 访问权限
  • 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:~#

在本次挑战中,您必须更改 /flag 的文件权限才能读取它! 通常,您需要具有文件的写入权限才能更改其权限,但是我已经使 chmod 命令在此关卡中非常强大,即使您是hacker用户,也可以 chmod 任何您想要的东西。 这是一种终极的力量。 /flag 文件归 root 所有,您无法更改它,但可以使其可读。 快来解决这个问题吧!

查看解析
chmod o+r /flag
cat /flag
使用"chmod"修改"/flag"其他用户的文件读取权限

Executable Files 可执行文件

到目前为止,您主要一直在处理读取权限。这是有道理的,因为您一直在使 /flag 文件可读以便读取它。在此关卡中,我们将探索执行权限。

当您调用程序(如 /challenge/run)时,Linux 只有在您具有程序文件的执行访问权限时才会实际执行该程序。考虑:

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:~$

在这种情况下,/challenge/run 会运行,因为它可由hacker用户执行。由于该文件归 root 用户和 root 组所有,因此这需要在其他用户的权限上设置执行权限。如果我们删除这些权限,执行将失败!

hacker@dojo:~$ chmod o-x /challenge/run
hacker@dojo:~$ ls -l /challenge/run
-rwxr-xr-- 1 root root    0 May 22 13:42 /challenge/run
hacker@dojo:~$ /challenge/run
bash: /challenge/run: Permission denied
hacker@dojo:~$

在这个挑战中,/challenge/run 程序会给你这个flag,但你必须先让它可执行!记住你的 chmod,然后获取 /challenge/run 来告诉你这面flag!

查看解析
chmod a+x /challenge/run
/challenge/run
使用"chmod"修改"/flag"所有用户的文件执行权限

Permission Tweaking Practice 权限调整练习

你觉得你可以 chmod?让我们练习吧!

此挑战将要求您连续几次以特定方式更改 /challenge/pwn 文件的权限。如果您获得错误的权限,游戏将重置,您可以重试。如果你连续八次获得正确的权限,挑战将让你 chmod /flag 使其对自己可读 :-)启动 /challenge/run 以开始挑战!

查看解析
按照题目提示修改`/challenge/pwn`文件的权限即可

Permission Setting Practice 权限设置练习

除了像上一关卡一样添加或删除权限之外,chmod 还可以简单地完全设置权限,覆盖旧的权限。这是通过使用 = 而不是 -+ 来完成的。例如:

  • u=rw 为用户设置读写权限,并擦除执行权限
  • o=x 仅设置其他用户的可执行权限,擦除 read 和 write 权限
  • a=rwx 为 user、group 和 world 设置读取、写入和可执行权限!

但是,如果您想以与组权限不同的方式更改用户权限,该怎么办?假设您想为拥有用户设置 rw,但只为拥有组设置 r?你可以通过使用

  • chmod u=rw,g=r /challenge/pwn 会将用户权限设置为读写,组权限设置为只读
  • chmod a=r,u=rw /challenge/pwn 会将用户权限设置为读写,将 group 和 world 权限设置为只读

此外,您可以使用 - 将权限归零:

  • chmod u=rw,g=r,o=- /challenge/pwn 将 User permissions 设置为 read 和 write,将 group permissions 设置为 read-only,并将 world 权限设置为完全不

请记住,出现在 = 之后的 - 与直接出现在 ugo 之后的上下文不同(在这种情况下,它会导致删除特定位,而不是所有内容)。

此关卡通过以上说的方式来更改权限,您需要 =, -来实现。祝你好运!

查看解析
与上一关一样,按照题目提示修改`/challenge/pwn`文件的权限即可

The SUID Bit SUID设置用户标识

在许多情况下,非 root 用户需要提升访问权限才能执行某些系统任务。系统管理员不能在每次用户想要执行只有 root/sudoers 才能执行的任务时都在那里给他们密码。“设置用户标识” (SUID) 权限位允许用户以程序文件的所有者身份运行程序。

这实际上是用于让您运行的挑战程序读取flag或在 pwn.college 之外启用系统管理工具(如 susudo 等)的确切机制。具有 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 [program]

但要小心!将 SUID 位提供给 root 拥有的可执行文件可能会为攻击者提供可能成为对 root 用户的攻击媒介。您将在 Program Misuse 模块中了解有关此内容的更多信息。

现在,我们将让您将 SUID 位添加到 /challenge/getroot 程序中,以便生成一个根 shell,以便您自己 cat flag!

查看解析
chmod u+s /challenge/getroot
/challenge/getroot
cat /flag

Untangling Users 理清用户关系

Becoming root with su 使用su成为root

在上一关中,您使用了 /challenge/getroot 程序成为 root 用户。成为 root 是 Linux 用户采取的一种相当常见的操作,而您的经典 Linux 显然没有 /challenge/getroot程序。相反,有两个实用程序用于此目的:susudo

在这个挑战中,我们将介绍较旧的 suswitch user 命令)。这通常不再用于提升为 root 访问权限,但它是来自更文明时代的优雅实用程序,我们将首先介绍它
su 是一个具有设置用户标识(setuid)权限的二进制文件:

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 密码,并且使用不同的机制(我们将在后面学习)来授予管理访问权限

但是在这个挑战中(也只有这个挑战中)确实有 root 密码。该密码是 hack-the-planet,您必须将其提供给 su 才能成为 root!去做那个,读一下flag!

查看解析
su
hack-the-planet
cat /flag
使用"su"程序提升到root权限

Other users with su 其他具有su的用户

在没有参数的情况下,su 将启动一个 root shell(在使用 root 的密码进行身份验证后)。但是,您也可以将 username 作为参数来切换到用户而不是 root。例如:

hacker@dojo:~$ su some-user
Password:
some-user@dojo:~$

很好!在这个关卡中,你必须切换到 zardus 用户,然后运行 /challenge/run。Zardus 的密码是 dont-hack-me。祝你好运!

查看解析
su zardus
dont-hack-me
/challenge/run
使用"su"程序切换到zardus用户

Cracking passwords 破解密码

当您输入 su 的密码时,它会将其与该用户的存储密码进行比较。这些密码曾经存储在 /etc/passwd 中,但因为 /etc/passwd 是一个全局可读的文件,这对密码不利,所以这些密码被移到了 /etc/shadow。下面是上一关的示例 /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$bEFkpM0w/6J0n979$47ksu/JE5QK6hSeB7mmuvJyY05wVypMhMMnEPTIddNUb5R9KXgNTYRTm75VOu1oRLGLbAql3ylkVa5ExuPov1. 是单向加密(哈希)Zardus 的密码的结果(在本例中为 don't-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 the Ripper(一款密码破解工具)破解了 Zardus 泄露的密码哈希,找到了 password1337 的真实值。可怜的Zardus!

此关卡模拟了这个示例,给你一个 /etc/shadow 的泄漏(在 /challenge/shadow-leak 中)。破解它(这可能需要几分钟),su 转换到 zardus,然后运行 /challenge/run 来获得flag!

查看解析
john /challenge/shadow-leak
su zardus
使用"john"工具破解zardus用户泄露的密码哈希值

【CTF入门】BUUCTF Misc刷题(持续更新) - Super_Snow_Sword - 博客园这里有使用Jhon破解密码的实战CTF题目

Using sudo 使用sudo

在过去,典型的Linux系统有一个root密码,管理员可以使用它来su到root(在使用正常的帐户密码登录到他们的帐户后)。但是根密码维护起来很麻烦,它们(或它们的哈希值!)可能会泄露,而且它们不适合大型环境(例如,服务器群)。为了解决这个问题,近几十年来,世界已经从通过su进行管理转向通过sudo(superuser do)进行管理。

与 su 不同,su 默认以指定用户身份启动 shell,而 sudo 默认以 root 身份运行命令:

hacker@dojo:~$ whoami
hacker
hacker@dojo:~$ sudo whoami
root
hacker@dojo:~$

或者,与获取flag更相关:

hacker@dojo:~$ grep hacker /etc/shadow
grep: /etc/shadow: Permission denied
hacker@dojo:~$ sudo grep hacker /etc/shadow
hacker:$6$Xro.e7qB3Q2Jl2sA$j6xffIgWn9xIxWUeFzvwPf.nOH2NTWNJCU5XVkPuONjIC7jL467SR4bXjpVJx4b/bkbl7kyhNquWtkNlulFoy.:19921:0:99999:7:::
hacker@dojo:~$

su 不同的是,sudo 不是通过密码进行身份验证,而是依赖于它检查的策略来确定用户以 root 身份运行程序的授权。这些策略在 /etc/sudoers 中定义,虽然它大多超出了我们的需要,但有大量资料可以学习这些!

因此,大部分用户转向使用 sudo 并 (出于系统管理目的) 将 su 抛在后面。事实上,即使是 pwn.college 的练习模式也是通过为您提供 sudo 访问权限来提升权限的!

在这个级别中,我们将为你提供 sudo 访问权限,你将使用它来读取flag。很好,很容易!


注意:在此级别之后,我们将启用练习模式!当您在 Practice Mode 中启动挑战时(通过单击 Practice 按钮而不是 Start 按钮),生成的容器将为您提供完整的 sudo 访问权限,以便您根据自己的喜好进行内省和调试,但当然会带有占位符标志。

查看解析
sudo cat /flag
使用"sudo"以root身份读取"flag"内容

Chaining Commands 命令串联

Chaining with Semicolons 使用分号链接

链接命令的最简单方法是 ;。在大多数上下文中,; 分隔命令的方式与 Enter 分隔行的方式类似。所以,在下例中:

hacker@dojo:~$ echo COLLEGE > pwn
hacker@dojo:~$ cat pwn
COLLEGE
hacker@dojo:~$

大致与此相同:

hacker@dojo:~$ echo COLLEGE > pwn; cat pwn
COLLEGE
hacker@dojo:~$

基本上,当您按 Enter 键时,shell 会执行您键入的命令,并在该命令终止后提示您输入另一个命令。分号与此类似,只是没有提示符,并且您在执行任何内容之前输入两个命令

现在就试一试吧!在这个关卡中,你必须运行 /challenge/pwn,然后运行 /challenge/college,用分号将它们连接起来

查看解析
/challenge/pwn; /challenge/college
使用`;`链接两个程序"

Building on Success 基于成功构建

你在进程模块中学习了退出代码。现在让我们使用它们来将命令链接在一起!

&& 操作符允许你仅在第一个命令成功时(按照 Linux 惯例,这意味着它以代码 0 退出)运行第二个命令。这被称为"AND"操作符,因为两个条件都必须为真:第一个命令必须成功 AND 然后第二个命令才会运行。这对于复杂的命令行工作流程非常有用,其中某些操作依赖于其他操作的成功。

语法如下:

hacker@dojo:~$ command1 && command2

这意味着:"运行 command1,如果它成功,则运行 command2。"

一些例子:

hacker@dojo:~$ touch /home/hacker/file && echo "this will run"
success
this will run
hacker@dojo:~$ touch /file && echo "this will NOT run"
touch: cannot touch '/file': Permission denied
hacker@dojo:~$

第二个 touch 调用失败了,因为 hacker 用户没有对 /file 的写入权限,所以 echo 没有运行。

在这个挑战中,你需要使用 && 操作符将程序 /challenge/first-success/challenge/second 链接在一起。先尝试单独运行每个命令,看看会发生什么(你将不会得到flag)。但如果你用 && 将它们链接起来,flag就会出现!

查看解析
/challenge/first-success && /challenge/second

Handling Failure 处理失败

你刚刚学习了 && 操作符,它仅在第一个命令成功时运行第二个命令。现在让我们学习它的相反操作:|| 操作符允许你仅在第一个命令失败(以非零代码退出)时运行第二个命令。这被称为"OR"操作符,因为要么第一个命令成功 OR 第二个命令将运行。

语法如下:

hacker@dojo:~$ command1 || command2

这意味着:"运行 command1,如果它失败,则运行 command2。"

一些例子:

hacker@dojo:~$ touch /file || echo "touch failed, so this runs"
touch: cannot touch '/file': Permission denied
touch failed, so this runs
hacker@dojo:~$ touch /home/hacker/file || echo "this will NOT run"
hacker@dojo:~$

|| 操作符对于提供备用命令或错误处理非常有用!

在这个挑战中,你需要使用 || 操作符将 /challenge/first-failure/challenge/second 链接在一起。去试试吧!

查看解析
/challenge/first-failure || /challenge/second

Your First Shell Script 你的第一个shell脚本

随着你组合越来越多的命令来实现复杂的效果,组合输入的长度很快就会变得非常烦人。发生这种情况时,您可以将这些命令放在一个称为 shell 脚本的文件中,然后通过执行该文件来运行它们!例如以下分号的使用:

hacker@dojo:~$ echo COLLEGE > pwn; cat pwn
COLLEGE
hacker@dojo:~$

我们可以创建一个名为 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:~$

您可以看到 shell 脚本执行了这两个命令,创建并打印了 pwn 文件。

现在,轮到你了!与上一关相同,运行 /challenge/pwn,然后运行 /challenge/college,但这次是在名为 x.sh 的 shell 脚本中运行,然后使用 bash 运行它!


注意:我们还没有讨论过 Linux 令人惊叹的一系列强大的命令行文件编辑器。现在,您可以随意使用桌面模式下的Text Editor应用程序 ( Applications->Accessories->Text Editor ) 或 VSCode 工作区中的默认编辑器!

查看解析
vim pwn.sh
/challenge/pwn
/challenge/college
bash pwn.sh
使用`vim`创建文本文件,并使用"esc"+":wq"保存并退出

Redirecting Script Output 重定向脚本输出

让我们尝试一些更棘手的事情!您已经使用 |在程序之间通过管道传输了输出,但到目前为止,这只是一个命令的输出和另一个命令的输入之间。但是,如果您想将多个程序的输出发送到一个命令,该怎么办?有几种方法可以做到这一点,我们将在这里探讨一个简单的方法:重定向脚本的输出!

就 shell 而言,您的脚本只是另一个命令。这意味着您可以重定向其输入和输出,就像在 Piping 模块中对命令所做的那样!例如,您可以将其写入文件:

hacker@dojo:~$ cat script.sh
echo PWN
echo COLLEGE
hacker@dojo:~$ bash script.sh > output
hacker@dojo:~$ cat output
PWN
COLLEGE
hacker@dojo:~$

所有的各种重定向方法都有效:>用于stdout,2>用于stderr,<用于stdin,>>2>>用于附加模式重定向,>&用于重定向到其他文件描述符,以及|用于管道到另一个命令。

在这个级别中,我们将练习从你的脚本到另一个程序的管道 (|)。和以前一样,您需要创建一个脚本,先调用 /challenge/pwn 命令,然后调用 /challenge/college 命令,并将脚本的输出通过管道传输到 /challenge/solve 命令的单个调用中!

查看解析
vim pwn.sh
/challenge/pwn
/challenge/college
bash pwn.sh | /challenge/solve

Executable Shell Scripts 可执行shell脚本

您已经编写了第一个 shell 脚本,但是通过 bash script.sh 调用它很痛苦。为什么需要那个 bash

调用 bash script.sh 时,您当然会启动带有 script.sh 参数的 bash 命令。这告诉 bash 从 script.sh 而不是标准输入中读取其命令,从而执行您的 shell 脚本。

事实证明,您可以避免手动调用 bash 的需要。如果您的 shell 脚本文件是可执行的(回想一下文件权限的内容),您只需通过其相对或绝对路径调用它即可!例如,如果您在主目录中创建 script.sh 并使其可执行,则可以通过 /home/hacker/script.sh 或 ~/script.sh 或(如果您的工作目录是 /home/hacker)./script.sh 调用它。

在这里试试吧!创建一个将调用 /challenge/solve 的 shellscript,使其可执行,并在不显式调用 bash 的情况下运行它!

查看解析
vim pwn.sh
/challenge/solve
chmod +x pwn.sh
./pwn.sh

Understanding Shebangs 理解 Shebang

你正顺利成为一名 Shell 脚本编写者!然而,到目前为止,你的 Shell 脚本只能从 Shell 启动。这在之前的关卡中运行良好(因为你是从 bash shell 调用脚本),但如果你的脚本是由例如用 Python(或任何其他语言)编写的程序调用,它们将无法工作。

在 Linux 中调用程序时,Linux 内核首先检查文件以确定应如何运行它。这并使用文件扩展名(这就是为什么你不.sh 扩展名命名 Shell 脚本,或用 .py 扩展名命名 Python 脚本,等等)。相反,Linux 查看文件开头的几个字节来获取这些信息。

程序有多种不同类型,但如果程序文件以字符 #! 开头(通常称为"shebang"),Linux 会将该文件视为解释型程序,并将该行其余部分的内容视为解释器的路径。然后,它调用该解释器,并将程序文件的路径作为其唯一参数。

考虑这个 Shell 脚本:

#!/bin/bash

echo "Hello Hackers!"

它可以这样执行:

hacker@dojo:~$ chmod a+x script.sh
hacker@dojo:~$ ./script.sh
Hello Hackers!
hacker@dojo:~$

当执行 ./script.sh 时,Linux 打开文件,读取第一行,提取 /bin/bash 作为解释器,然后执行 /bin/bash ./script.sh 来启动脚本!

注意,shebang 行必须是文件的第一行 - 其前面不能有空行或空格!

在这个挑战中,请在 /home/hacker/solve.sh 创建一个脚本,该脚本包含正确的 shebang 并输出 "hack the planet"。记得使其可执行,然后运行 /challenge/run 来验证你的脚本是否正确工作!


有趣的事实: 你可能会看到的常见 shebang:

  • #!/bin/bash 用于 bash 脚本
  • #!/usr/bin/python3 用于 Python 脚本
  • #!/bin/sh 用于 POSIX shell 脚本 --- 这是 bash 的一个更原始的前身,功能较少,但在非 Linux 系统上兼容性更好!
查看解析
vim /home/hacker/solve.sh
#!/bin/bash
echo "hack the planet"
chmod +x /home/hacker/solve.sh
./solve.sh
/challenge/run

Scripting with Arguments 带参数的脚本编写

你已经学会了如何制作 Shell 脚本,但到目前为止它们还只是命令列表。当脚本能够接受参数时,它们会变得更加强大!这可能看起来像:

hacker@dojo:~$ bash myscript.sh hello world

脚本可以使用特殊变量访问这些参数:

  • $1 包含第一个参数 ("hello")
  • $2 包含第二个参数 ("world")
  • $3 将包含第三个参数(如果有的话)
  • ...依此类推

这是一个简单的例子:

hacker@dojo:~$ cat myscript.sh
#!/bin/bash
echo "First argument: $1"
echo "Second argument: $2"
hacker@dojo:~$ bash myscript.sh hello world
First argument: hello
Second argument: world
hacker@dojo:~$

对于这个挑战,你需要在 /home/hacker/solve.sh 编写一个脚本,该脚本:

  1. 接受两个参数
  2. 相反的顺序输出它们(先输出第二个参数,然后输出第一个参数)

例如:

hacker@dojo:~$ bash /home/hacker/solve.sh pwn college
college pwn
hacker@dojo:~$

一旦你的脚本正确工作,运行 /challenge/run 来获取你的flag!

查看解析
vim /home/hacker/solve.sh
#!/bin/bash
echo "$2 $1"
/challenge/run

Scripting with Conditionals 带条件判断的脚本编写

既然你可以在脚本中使用参数,现在让我们通过条件逻辑使它们变得更智能!

在 bash 中,你可以使用 if 语句来做决策:

if [ "$1" == "ping" ]
then
    echo "pong"
fi

上面代码的意思是:如果第一个参数是 "ping",则打印出 "pong"。由于几个原因,语法有些严格。首先,你必须if 后、[ 后和 ] 前有空格(如果你习惯像 C 这样的语言,这是不同的)。其次,ifthenfi 必须都在不同的行上(或用分号分隔);你不能将它们混在同一个语句中。也有点奇怪:if 语句的结束符不是 endifend 之类的,而是 fiif 倒过来写)。这是你必须记住的东西。

对于这个挑战,在 /home/hacker/solve.sh 编写一个脚本,该脚本:

  • 接受一个参数
  • 如果参数是 "pwn",输出 "college"
  • 对于任何其他输入,不输出任何内容

示例:

hacker@dojo:~$ bash /home/hacker/solve.sh pwn
college
hacker@dojo:~$ bash /home/hacker/solve.sh foo
hacker@dojo:~$

一旦你的脚本正确工作,运行 /challenge/run 来获取你的flag!


注意: 想了解在条件判断中除了字符串相等之外还能检查什么吗?请使用 help test 阅读所有相关内容!

查看解析
vim /home/hacker/solve.sh
#!/bin/bash
if [ "$1" == "pwn" ]
then
    echo "college"
fi
/challenge/run

Scripting with Default Cases 带默认情况的脚本编写

你目前的 if 语句处理了特定情况,但其他所有情况呢?这时 else 就派上用场了!

else 子句在 if 条件为假时执行:

if [ "$1" == "hello" ]
then
    echo "Hi there!"
else
    echo "I don't understand"
fi

请注意,else 没有条件 --- 它捕获所有之前不匹配的情况。它也没有 then 语句。最后,fi 放在 else 块之后,表示整个复杂语句的结束!它也是可选的:你在上一关中不需要它,只有当你要实现的逻辑要求时才需要它。

这是一个实际的例子:

if [ "$1" == "start" ]
then
    echo "Starting the service..."
else
    echo "Unknown command. Use 'start' to begin."
fi

对于这个挑战,在 /home/hacker/solve.sh 编写一个脚本,该脚本:

  • 接受一个参数
  • 如果参数是 "pwn",输出 "college"
  • 对于任何其他输入,输出 "nope"

示例:

hacker@dojo:~$ bash /home/hacker/solve.sh pwn
college
hacker@dojo:~$ bash /home/hacker/solve.sh hack
nope
hacker@dojo:~$ bash /home/hacker/solve.sh anything
nope
hacker@dojo:~$

一旦你的脚本正确工作,运行 /challenge/run 来获取你的flag!

查看解析
vim /home/hacker/solve.sh
#!/bin/bash
if [ "$1" == "pwn" ]
then
    echo "college"
else
    echo "nope"
fi
/challenge/run

Scripting with Multiple Conditions 带多重条件判断的脚本编写

你已经学会了如何使用单个 if 语句来检查一个条件。但是如果你需要检查多个条件呢?你可以使用 elifelse if 的缩写):

if [ "$1" == "one" ]
then
    echo "1"
elif [ "$1" == "two" ]
then
    echo "2"
elif [ "$1" == "three" ]
then
    echo "3"
else
    echo "unknown"
fi

请注意,你确实需要在 elif 后面加一个 then,就像 if 一样。和之前一样,最后的 else 捕获所有不匹配的情况。

对于这个挑战,在 /home/hacker/solve.sh 编写一个脚本,该脚本:

  • 接受一个参数
  • 如果参数是 "hack",输出 "the planet"
  • 如果参数是 "pwn",输出 "college"
  • 如果参数是 "learn",输出 "linux"
  • 对于任何其他输入,输出 "unknown"

示例:

hacker@dojo:~$ bash /home/hacker/solve.sh hack
the planet
hacker@dojo:~$ bash /home/hacker/solve.sh pwn
college
hacker@dojo:~$ bash /home/hacker/solve.sh learn
linux
hacker@dojo:~$ bash /home/hacker/solve.sh foo
unknown
hacker@dojo:~$

一旦你的脚本正确工作,运行 /challenge/run 来获取你的flag!


注意: 在创建脚本时,请确保严格遵循示例中的空格。与许多其他语言不同,bash 要求 [] 与其他字符之间用空格分隔,否则它无法解析条件。

查看解析
vim /home/hacker/solve.sh
#!/bin/bash
if [ "$1" == "hack" ]
then
    echo "the planet"
elif [ "$1" == "pwn" ]
then
    echo "college"
elif [ "$1" == "learn" ]
then
    echo "linux"
else
    echo "unknown"
fi
/challenge/run

Reading Shell Scripts 阅读 Shell 脚本

并不是只有你一个人编写 Shell 脚本!它们对于执行简单的"系统级"任务非常方便,是开发人员和系统管理员常用的工具。事实上,典型 Linux 机器上相当大一部分程序都是 Shell 脚本。

在这一关中,我们将学习阅读 Shell 脚本。/challenge/run 是一个 Shell 脚本,它要求你输入一个秘密密码,但该密码是硬编码在脚本本身的!阅读该脚本(例如使用 cat),找出密码,然后获取flag!


注意: 欢迎你也尝试阅读其他挑战的代码!阅读代码是学习新技能的关键策略,因为你可以看到某些功能是如何实现的,并在你自己的脚本中重用这些策略。但请注意:有些程序文件是机器码,人类无法阅读。你可以使用 file 命令来区分,但 Linux Luminarium 中几乎所有的挑战都是作为 Shell 脚本实现的,并且可以安全地用 cat 查看。

查看解析
cat /challenge/run可见:
#!/opt/pwn.college/bash
read GUESS
if [ "$GUESS" == "hack the PLANET" ]
then
        echo "CORRECT! Your flag:"
        cat /flag
else
        echo "Read the /challenge/run file to figure out the correct password!"
fi
密码为hack the PLANET
/challenge/run
hack the PLANET

Terminal Multiplexing 终端多路复用

Launching Screen 启动 Screen

让我们直接开始吧!

screen 是一个在你的终端内创建虚拟终端的程序。它有点像拥有多个浏览器标签页,但是用于你的命令行!

启动 screen 非常简单:

hacker@dojo:~$ screen

就这样!你现在已经在一个 screen 会话中了。它看起来完全像一个终端,但那里有新的功能,等待被发现。

对于这个挑战,我们已经设置好,只要启动 screen 就能获得flag。很简单!


注意: 当你完成命令行操作后,输入 exit 或按 Ctrl-D 来离开 screen 会话。然后 screen 将终止并返回到你原始的 shell。

查看解析
screen

Detaching and Attaching 分离和重新连接

现在我们将开始探索分离的魔力!

想象一下,你正在通过远程连接处理一些重要的工作,然后你的连接中断了。使用普通终端(在这个 awesome dojo 环境之外),所有东西都消失了。但使用 screen,你的工作会继续运行,并且你可以稍后重新连接

你也可以故意分离,我们将在本挑战中这样做。你可以通过按 Ctrl-A,然后按 d(代表 detach,分离)来分离。这会让你的会话在后台继续运行,同时你返回到正常终端。

hacker@dojo:~$ screen
[doing some work...]
[Press Ctrl-A, then d]
[detached from 12345.pts-0.hostname]
hacker@dojo:~$ 

重新连接,你可以使用 screen-r 参数:

hacker@dojo:~$ screen -r

对于这个挑战,你需要:

  1. 启动 screen
  2. 从中分离。
  3. 运行 /challenge/run(这会将flag秘密发送到你已分离的会话!)
  4. 重新连接以查看你的奖励

有趣的事实: 在默认配置中,Ctrl-Ascreen 所有快捷方式的激活键。所有 screen 功能都是通过以 Ctrl-A 开头的某个命令组合来激活的。

提示: 记住:按住 Ctrl 并按 A,然后松开两者再按 d。

提示: 如果你看到 [detached from...],说明你做对了!

查看解析
screen
《组合按键 Ctrl+A, 然后按d》
/challenge/run
screen -r
"screen -r"命令用于返回screen

Finding Sessions 查找会话

是时候做一些 screen 侦探工作了!

如果你成为一个 screen 的活跃用户,最终不可避免地会运行多个会话。如何找到正确的会话来重新连接呢?

我们可以列出它们:

hacker@dojo:~$ screen -ls
There are screens on:
        23847.mysession   (Detached)
        23851.goodwork    (Detached)
        23855.morework    (Detached)
3 个 Socket 在 /run/screen/S-hacker。

会话的标识符是各自 screen 进程的 PID,一个点,以及 screen 会话的名称。要连接到特定的会话,你可以使用它的名称或 PID 作为 screen -r 的参数。

hacker@dojo:~$ screen -r goodwork

在这个挑战中,我们为你创建了三个 screen 会话。其中一个包含flag。另外两个是诱饵!

你需要检查每一个直到找到它。在尝试下一个会话之前,别忘了分离(Ctrl-A d)!

查看解析
screen -ls
screen -r 146

Switching Windows 切换窗口

好了,到目前为止,screen 只是一种有点奇怪的"终端中的终端"。但它可以做得更多!

在单个 screen 会话中,你可以有多个窗口,就像你的浏览器有多个标签页一样。这对于组织不同的任务非常方便!

这些窗口通过不同的键盘快捷键来处理,都以 Ctrl-A 开头:

  • Ctrl-A c - 创建一个新窗口
  • Ctrl-A n - 下一个窗口
  • Ctrl-A p - 上一个窗口
  • Ctrl-A 0Ctrl-A 9 - 直接跳转到窗口 0-9
  • Ctrl-A " - 调出所有窗口的选择菜单

对于这个挑战,我们设置了一个包含两个窗口的 screen 会话:

  • 窗口 0 有...好吧,你得切换过去才能发现!
  • 窗口 1 有一条欢迎信息

使用 screen -r 连接到会话,然后使用上面的某个组合键切换到窗口 1。去获取那个flag吧!

查看解析
screen -ls
screen -r 137
《组合按键 Ctrl+A, 然后按0》

Detaching and Attaching (tmux) 分离和重新连接 (tmux)

让我们用 tmux 试试同样的操作!

tmux(终端多路复用器)是 screen 更年轻、更现代的"表亲"。它做所有相同的事情,但使用一些不同的键绑定。最大的区别?tmux 使用 Ctrl-B 作为其命令前缀,而不是 Ctrl-A

所以要脱离 tmux,你需要按 Ctrl-B,然后按 d

hacker@dojo:~$ tmux
[doing some work...]
[Press Ctrl-B, then d]
[detached (from session 0)]
hacker@dojo:~$ 

命令也不同:

  • tmux ls - 列出会话
  • tmux attachtmux a - 重新连接到会话

对于这个挑战:

  1. 启动 tmux
  2. 从中分离。
  3. 运行 /challenge/run(这会将flag发送到你已分离的会话!)
  4. 重新连接以查看你的奖励
查看解析
tmux
《组合按键 Ctrl+B, 然后按d》
/challenge/run
tmux a

Switching Windows (tmux) 切换窗口 (tmux)

让我们学习在 tmux 中导航窗口!

就像 screen 一样,tmux 也有窗口。组合键不同,但概念是一样的:

  • Ctrl-B c - 创建一个新窗口
  • Ctrl-B n - 下一个窗口
  • Ctrl-B p - 上一个窗口
  • Ctrl-B 0Ctrl-B 9 - 跳转到窗口 0-9
  • Ctrl-B w - 查看一个不错的窗口选择器

Tmux 在底部的状态栏中显示你的窗口,看起来像:

[0] 0:bash* 1:bash

* 显示你当前的窗口,每个条目还显示创建窗口时运行的进程。

我们创建了一个包含两个窗口的 tmux 会话:

  • 窗口 0 有flag!
  • 窗口 1 有你的热烈欢迎。

去获取那个flag吧!

查看解析
tmux a
《组合按键 Ctrl+B, 然后按0》

Pondering PATH 对PATH环境变量的深入思考

The PATH Variable 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 命令。

在这个关卡中,您将破坏 /challenge/run 程序的运行。该程序将使用 rm 命令删除flag文件。但是,如果找不到 rm 命令,则不会删除该flag,挑战会将其交给您!因此,您必须使 /challenge/run 也找不到 rm 命令!

请记住:/challenge/run 将是 shell 的子进程,因此您必须应用在 Shell 变量中学到的概念来弄乱其 PATH 变量!如果您没有成功,并且标记被删除,您将需要重新开始挑战才能重试!

查看解析
PATH=""
/challenge/run
使PATH环境变量为空使得"run"程序无法运行`rm`

Setting PATH 设置PATH环境变量

好的,所以当你清空 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:~$

让我们练习一下。此关卡的 /challenge/run 将通过其裸名称运行 win 命令,但此命令存在于 /challenge/more_commands/ 目录中,该目录最初并不在 PATH 中。win 命令是 /challenge/run 唯一需要的东西,所以你可以用这个目录覆盖 PATH。祝你好运!

查看解析
PATH="/challenge/more_commands"
/challenge/run
设置PATH环境变量为空使得"run"程序能够直接裸名称运行`win`命令

Finding Commands 查找命令

当你输入一个命令的名称时,实际上执行的是 $PATH 变量中列出的众多目录中的某个文件(当然,除非该命令是 shell 内建命令!)。但具体是哪个文件呢?你可以使用恰如其名的 which 命令来找出答案:

hacker@dojo:~$ which cat
/bin/cat
hacker@dojo:~$ /bin/cat /flag
YEAH
hacker@dojo:~$

与 Shell 在搜索命令时的行为一致,which 会按顺序查看 $PATH 中的每个目录,并打印出它找到的第一个与你传入的参数名称匹配的文件。

在这个挑战中,我们在你的 $PATH 中的某个位置添加了一个 win 命令,但它不会直接给你flag。相反,flag位于与 win 命令相同的目录下的一个 flag 文件中,并且我们已将该文件设置为你可读!你必须找到 win 命令(使用 which 命令),然后从该目录中 cat 出flag文件!

查看解析
which win
ls /challenge/paths/26226/
cat /challenge/paths/26226/flag

Adding Commands 添加命令

回想一下上一关的示例:

hacker@dojo:~$ ls /home/hacker/scripts
goodscript	badscript	okayscript
hacker@dojo:~$ PATH=/home/hacker/scripts
hacker@dojo:~$ goodscript
YEAH! This is the best script!
hacker@dojo:~$

当然,我们在这里看到的是,黑客通过将自己的命令带到PATH环境变量中,使 shell 对自己更有用。随着时间的推移,您可能会积累自己的优雅工具。让我们从 win 开始吧!

之前,/challenge/run 执行的 win 命令存储在 /challenge/more_commands 中。这一次,win不存在!回顾链接命令的最后一关,并创建一个名为 win 的 shell 脚本,将其位置添加到 PATH中,然后启用 /challenge/run 来找到它!


提示:/challenge/run 以 root 身份运行,并将调用 win。因此,win 可以简单地 cat flag文件。同样,win 命令是 /challenge/run 唯一需要的东西,所以你可以用这个目录覆盖 PATH。但请记住,如果你这样做,你的 win 命令将无法找到 cat

您有三种选择可以避免这种情况:

  1. 找出 cat 程序在文件系统上的位置。它必须位于 PATH 变量中的目录中,因此您可以打印出变量(请参阅 Shell 变量以记住如何操作!),并浏览其中的目录(回想一下,不同的条目用 分隔),找到哪个条目中有 cat,并通过其绝对路径调用 cat
  2. 设置一个 PATH,其中包含旧目录新条目,用于您创建 win 的位置。
  3. 使用 read (再次参考 Shell 变量) 来读取 /flag。由于 readbash 的内置功能,因此它不受 PATH 恶作剧的影响。

现在,开始win吧!

查看解析
这里使用第二种方法,设置一个包含旧目录和新目录的PATH环境变量
vim win
chmod +x win
echo $PATH
export PATH=/home/hacker:$PATH
/challenge/run
使用"export"命令将新添加的PATH环境变量添加到源PATH环境变量前面

Hijacking Commands 劫持命令

有了现在的知识,你现在可以进行一些恶作剧了。此挑战与本模块中的第一个挑战几乎相同。同样,此挑战将使用 rm 命令删除flag。但与以前不同的是,它不会为您打印任何内容

你怎么解决这个问题呢?您知道 rm 是在 PATH 变量中列出的目录中搜索的。您有在上一个挑战需要时创建 win 命令的经验。您还可以创建哪些内容?

查看解析
vim rm
chmod +x rm
export PATH=/home/hacker:$PATH
/challenge/run
使PATH环境变量为空使得"run"程序无法运行`rm`

Silly Shenanigans 恶作剧与诡计

Bashrc Backdoor Bashrc 后门

当你的 Shell 启动时,它会在你的家目录中寻找 .bashrc 文件,并将其作为启动脚本执行。你可以用一些有用的东西来自定义你的 /home/hacker/.bashrc,例如设置环境变量、调整 Shell 配置等等。

你也可以用它来做坏事!不知情受害者的 .bashrc 是恶作剧的常见目标。想象一下偷偷潜入你朋友的电脑,在他们的 .bashrc 末尾添加一行 echo "Hackers were here!"。这很有趣,但同样的能力也可以用于更邪恶的目的。例如,恶意软件经常以诸如 .bashrc 之类的启动脚本为目标,以维持未来的持久性!

在这个挑战中,我们将假装你已经入侵了一个受害用户的机器!该用户名为 zardus,家目录是 /home/zardus。你作为 hacker 用户,拥有对他 .bashrc 的写入权限,并且 zardus/flag 有读取权限。受害者由脚本 /challenge/victim 模拟,你可以随时启动此脚本来观察受害者登录计算机。你能拿到flag吗?


提示: 就像你在命令链中探索的脚本一样,.bashrc 脚本只是一个 Shell 脚本。添加一个新行并在上面写一个命令(例如,echo Hello Hackers)将执行该命令,所以你真正需要思考的是用什么命令能让你获得flag!

注意: 受害者的 /home/zardus/.bashrc 中已经有很多内容了:Shell 的启动过程很复杂。不要惊慌——只需将你的有效载荷添加到末尾并期待最好的结果!

提示: 需要以 zardus 身份四处调试你的解决方案吗?在特权模式下,你可以使用 sudo su --login zardus 来进入一个 Zardus 会话!

查看解析
vim /home/zardus/.bashrc
cat /flag
/challenge/victim

Sniffing Input 嗅探输入

在上一关中,你滥用了 Zardus 的 ~/.bashrc 让他为你运行命令。

这次,Zardus 在登录后不会将flag留在可读文件中。相反,他会运行一个名为 flag_checker 的命令,手动将flag输入其中进行验证。

你的任务是利用你对 Zardus 的 .bashrc 的持续写入权限来拦截这个flag。还记得你在思考 PATH 模块中是如何劫持命令的吗?你能利用这个能力来劫持 flag_checker 吗?


提示: Zardus 是否被你的劫持吓到了?他很小心——他会检查 flag_checker 的提示 Type the flag。确保你的劫持也打印这个提示(例如,echo "Type the flag")。除了打印该提示外,你的假 flag_checker 可以:a) 直接将 Zardus 的输入 cat 到标准输出(例如,不带参数的 cat),或者 b) 用 read 将其读入变量并 echo 出来。由你决定!


提示: 别忘了让你的假 flag_checker 可执行,就像你在理解权限模块中学到的那样!

查看解析
vim /home/zardus/.bashrc
PATH=/home/hacker
在自己的目录中创建脚本"flag_checker":
#!/bin/bash
echo "Type the flag"
read flag
echo $flag
/challenge/victim

Overshared Directories 过度共享的目录

好吧,Zardus 变聪明了——他为什么要有一个可写的 .bashrc 呢?但更常见的情况是,同一系统上的用户为了便于协作,会将他们的家目录设置为全局可写。这有什么问题呢?

问题是 Linux 文件/目录权限的一个微妙之处在于,任何对目录有写权限的人都可以移动删除其中的文件。例如,假设 Zardus 有一个用于协作的全局可写目录:

zardus@dojo:~$ mkdir /tmp/collab
zardus@dojo:~$ chmod a+w /tmp/collab
zardus@dojo:~$ echo "do pwn.college" > /tmp/collab/todo-list

然后一个黑客过来做了以下操作,尽管他不拥有 todo-list 文件

hacker@dojo:~$ ls -l /tmp/collab/todo-list
-rw-r--r-- 1 zardus zardus 15 Jun  6 13:12 /tmp/collab/todo-list
hacker@dojo:~$ rm /tmp/collab/todo-list
rm: remove write-protected regular file '/tmp/collab/todo-list'? y
hacker@dojo:~$ echo "send hacker money" > /tmp/collab/todo-list
hacker@dojo:~$ ls -l /tmp/collab/todo-list
-rw-r--r-- 1 hacker hacker 18 Jun  6 13:12 /tmp/collab/todo-list
hacker@dojo:~$

这可能看起来有违直觉:hackertodo-list 没有写权限,但最终结果却是他们可以更改内容。但这样想:文件与目录的连接最终存在于目录中,对该目录有写权限的用户可以对其进行操作。当然,当重要目录是全局可写时,这会带来安全隐患。

在这个挑战中,为了方便起见,Zardus 开放了他的家目录:

zardus@dojo:~$ chmod a+w /home/zardus

如你所知,该目录中有许多敏感文件,例如 .bashrc!你能用对 /home/zardus 的写权限(而不是 /home/zardus/.bashrc)复制之前的攻击吗?

查看解析
rm /home/zardus/.bashrc
vim /home/zardus/.bashrc
PATH=/home/hacker
在自己的目录中创建脚本"flag_checker":
#!/bin/bash
echo "Type the flag"
read flag
echo $flag
/challenge/victim

Tricky Linking 棘手的链接

好吧,Zardus 变聪明了!不再共享家目录:尽管不方便,Zardus 转而共享 /tmp/collab。他使该目录全局可读,并开始列一个要记住的邪恶命令列表!

zardus@dojo:~$ mkdir /tmp/collab
zardus@dojo:~$ chmod a+w /tmp/collab
zardus@dojo:~$ echo "rm -rf /" > /tmp/collab/evil-commands.txt

在这个挑战中,当你运行 /challenge/victim 时,Zardus 会将 cat /flag 添加到那个命令列表中:

hacker@dojo:~$ /challenge/victim

Username: zardus
Password: **********
zardus@dojo:~$ echo "cat /flag" >> /tmp/collab/evil-commands.txt
zardus@dojo:~$ exit
logout

hacker@dojo:~$

回顾上一关,拥有对 /tmp/collab 的写权限,hacker 用户可以替换那个 evil-commands.txt 文件。同时记得在理解命令中,文件可以链接到其他文件。如果 hackerevil-commands.txt 替换为一个指向 zardus 可以写入的某个敏感文件的符号链接,会发生什么?混乱和恶作剧!

知道要链接到哪个文件。执行这个攻击,并获取 /flag(在这个关卡中,Zardus 又可以读取它了!)。


提示: 你需要运行 /challenge/victim 两次:一次让 cat /flag 写入你想要的位置,一次来触发它!

使用 /tmp 危险吗??? 尽管这里展示了攻击,但 /tmp 可以安全使用。该目录确实是全局可写的,但设置了一个特殊的权限位:

hacker@dojo:~$ ls -ld /tmp
drwxrwxrwt 29 root root 1056768 Jun  6 14:06 /tmp
hacker@dojo:~$

末尾的 t 位是粘滞位。粘滞位意味着该目录只允许文件的所有者重命名或删除目录中的文件。它就是为了防止这种确切的攻击而设计的!当然,这个挑战中的问题是 Zardus 没有在 /tmp/collab 上启用粘滞位。这本来可以堵住这个特定的漏洞:

zardus@dojo:~$ chmod +t /tmp/collab

当然,像全局可写目录这样的共享资源仍然是危险的。很久以后,在绿带材料的竞争条件中,你会看到许多此类资源导致安全问题的方式!

查看解析
rm /tmp/collab/evil-commands.txt
ln -s /home/zardus/.bashrc /tmp/collab/evil-commands.txt
/challenge/victim 
/challenge/victim 
第一次:追加 "cat /flag" 到 .bashrc
第二次:当 zardus 登录时,.bashrc 被执行,所以 cat /flag 运行,输出 flag。

Sniffing Process Arguments 嗅探进程参数

可怜的 Zardus;你狠狠地黑了他。但他已经醒悟过来,并保护了他的家目录!游戏结束了吗?

未必!当一台计算机上有多个账户时,人们通常不会考虑他们的命令调用会泄露什么样的数据。记住,当你执行 ps aux 时,你会得到:

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:~$

但是如果这些命令的某个参数是敏感数据,比如flag或密码,会发生什么?这种情况确实会发生,而共享同一台机器(或以其他方式列出其上进程)的恶意用户可以窃取这些数据并利用它!

这就是这个挑战要探索的。Zardus 正在使用一个自动化脚本,并将他的账户密码作为参数传递给它。Zardus 也被允许使用 sudo(因此,可以 sudo cat /flag!)。窃取密码,登录到 Zardus 的账户(回想一下理清用户模块中的 su 命令),然后获取那个flag!

查看解析
ps aux
可以看到密码
su --login zardus
sudo cat /flag

Snooping on Configurations 窥探配置

即使不犯错,用户也可能在无意中将自己置于风险之中。例如,典型用户家目录中的许多文件默认是全局可读的,尽管它们经常被用来存储敏感信息。信不信由你,你的 .bashrc 就是全局可读的,除非你明确更改它!

hacker@dojo:~$ ls -l ~/.bashrc
-rw-r--r-- 1 hacker hacker 148 Jun  7 05:56 /home/hacker/.bashrc
hacker@dojo:~$

你可能会想,"嘿,至少它默认不是全局可写的"!但即使是全局可读,也可能造成损害。由于 .bashrc 在启动时由 Shell 处理,人们通常会在那里放置他们想要自定义的任何环境变量的初始化。大多数时候,这是一些无害的东西,比如 PATH,但有时人们会在那里存储 API 密钥以便于访问。例如,在这个挑战中:

zardus@dojo:~$ echo "FLAG_GETTER_API_KEY=sk-XXXYYYZZZ" > ~/.bashrc

之后,Zardus 可以轻松地引用该 API 密钥。在这个关卡中,用户可以使用有效的 API 密钥来获取flag:

zardus@dojo:~$ flag_getter --key $FLAG_GETTER_API_KEY
Correct API key! Do you want me to print the key (y/n)? y
pwn.college{HACKED}
zardus@dojo:~$

自然,Zardus 将他的密钥存储在 .bashrc 中。你能窃取密钥并获取flag吗?


注意: 当你获取到 API 密钥后,只需以 hacker 用户身份执行 flag_getter。这个挑战的 /challenge/victim 只是为了主题设定:你不需要使用它。

查看解析
cat /home/zardus/.bashrc
flag_getter --key sk-112942840

Daring Destruction 毁灭性操作

The Fork Bomb 进程炸弹

正如你在进程与作业模块中学到的,每当你启动一个程序时,Linux 操作系统都会创建一个新进程。如果你创建进程的速度超过内核处理能力,进程表就会被填满,一切都会陷入停顿。这个新进程(例如,一个 ls 调用)是从父进程(例如,一个 Shell 实例)"派生"出来的。因此,这种引发的进程激增被称为"进程炸弹"。

你拥有实现这个目标的工具:

  • 编写一个小脚本(就像在命令链模块中一样)
  • 使其可执行(就像在理解权限模块中一样)
  • 让它在后台启动自身的副本(就像在进程与作业模块中一样)
  • 然后在后台启动另一个自身的副本!

每个副本将再启动两个,每个新副本又将启动两个,你将用如此多的进程淹没系统,以至于新的进程将无法启动!

这个挑战包含一个 /challenge/check,它会尝试判断你的进程炸弹是否在起作用(例如,如果它无法启动新进程),如果是,就会给你flag。确保发动攻击之前(在另一个终端中)启动它;否则你将无法启动它!


注意: 不用说,这会使你的环境无法使用。只需重启挑战(或开始一个不同的挑战)即可让一切恢复到可用状态!

查看解析
/challenge/check
(开启新终端)
vim bomb
#!/bin/bash
./bomb &
./bomb &
(加上`&`用于后台运行)
chmod +x bomb
./bomb
(要等一段时间,稍安勿躁)

Disk-Space Doomsday 磁盘空间末日

这个容器中 /home/hacker 的可用空间只有可怜的 1 GB。在这个关卡中,你将用大量垃圾堵塞 /home/hacker,以至于甚至无法创建一个微小的 1 MB 文件。当这种情况发生时,你的工作空间将变得无法使用。我们将在这个挑战中练习诱发这种情况,然后稍后对其进行扩展。

如何填满磁盘?方法有很多。这里,我们将教你 yes 命令!

hacker@dojo:~$ yes | head
y
y
y
y
y
y
y
y
y
y
hacker@dojo:~$

yes 会永远重复输出 y。典型的用法是通过管道自动确认提示("你确定要删除这个文件吗?"),但我们将在这里用它来制作一个充满 "y" 行的巨大文件。只需将 yes 重定向到你家目录中的一个文件,你就能在一两分钟内填满你的磁盘!

这个挑战迫使你填满磁盘然后清理。过程如下:

  1. 填满你的磁盘。
  2. 运行 /challenge/check。它会尝试创建一个 1 MB 的临时文件。如果失败,你就通过了第一阶段,检查器会要求你释放空间。
  3. 删除你创建的文件(使用 rm)以清理空间。
  4. 再次运行 /challenge/check。如果它现在可以创建临时文件(即,你成功清理了你的家目录),你将收到flag。

为什么分两个阶段? 你的家目录在挑战实例之间是持久化的。如果我们让你保持磁盘满的状态,你的 pwn.college 将停止工作。这是迄今为止 pwn.college 上出现奇怪问题的最常见原因!

救命,它坏了! 如果你填满了磁盘但没有事后清理,你需要通过 ssh 连接来修复(通过删除那个文件)。这有点棘手,但我们在入门模块的"通过 SSH 连接"下描述了如何操作。

查看解析
yes > nihao.txt
/challenge/check
rm nihao.txt
/challenge/check

rm -rf /

想彻底清除一切并重新开始吗?你可以!

console

hacker@dojo:~$ ls /
bin etc blah blah blah
hacker@dojo:~$ rm -rf /
hacker@dojo:~$ ls /
bash: ls: command not found
hacker@dojo:~$

这里发生了什么?如你所知,rm 删除文件。-r(递归)flag删除目录及其包含的所有文件。-f(强制)flag忽略 rm 命令可能遇到的任何错误或提示。将两者结合并指向 /,结果是灾难性的:系统被完全清空。在现代系统上,事情没那么简单,但当你看到时你就会明白。

在这个挑战中,你将做一些你可能再也不会做的事情:清除整个系统。我们实际上稍微修改了一些东西来保护你的家目录(通常情况下,它也会被清除!),但除此之外,你和flag之间唯一的障碍就是你清空驱动器的意愿。但在你清空一切之前,确保启动 /challenge/check,以便它能观看这场"烟花表演"(并给你flag)!


注意: rm 需要一段时间才能运行完毕。有很多东西要删除!

注意: 由于各种技术原因,你不太可能删除所有东西,包括我们在这个关卡中用来保护你家目录的技术。别担心,你造成的破坏已经足够了!

查看解析
/challenge/check
(开启新终端)
rm -rf --no-preserve-root /
【--no-preserve-root: 特别针对根目录 /的保护绕过选项】

rm -rf / 之后的生活

让我们深入探究清空整个文件系统的影响。你现在是一个经验丰富的 rm 使用者了,但之前,/challenge/check 在你清除了文件系统的混乱后为你打印了flag。如果它没有呢?没有 cat,你怎么读取那个 /flag

回想一下,在消化文档模块中,一些 Shell 命令是内建命令。虽然 lscat 等不是,但 read(如果你还记得Shell 变量模块,它可以读取文件!)。这意味着,即使你清除了整个文件系统,只要你有一个已经在运行的 bash 实例,你就可以读取文件!

这个挑战将迫使你尝试它。它几乎和前一个一样,但你必须在自己摧毁系统后读取flag。在你 rm 一切之后,你之前启动的 /challenge/check 将恢复 /flag 文件并使其可读。然后你就可以 read 它了!

查看解析
/challenge/check
(开启新终端)
rm -rf --no-preserve-root /
read flag < /flag
$flag

rm -rf / 之后寻找意义

所以没有 cat 你也能活!那没有 ls 呢?这次,/challenge/check 会将flag恢复到一个随机命名的文件中。你将需要在没有 ls 命令的情况下找到它。

解决这个挑战的方法有很多。echo 是一个内建命令,你可以使用文件通配将参数扩展为所有文件!例如,echo * 将打印出当前目录中所有文件的名称。类似地,你可以使用参数的 Tab 键补全(按几次 Tab 键)让 Shell 为你列出可能的文件。

无论你使用哪种方法,找到 /challenge/check 在你 rm 之后在 / 中创建的随机命名文件,读取它,然后获取flag!

查看解析
/challenge/check
(开启新终端)
rm -rf --no-preserve-root /
echo /*
posted @ 2024-10-20 22:59  Super_Snow_Sword  阅读(1276)  评论(0)    收藏  举报