【笔记】CSE 365 - Fall 2024之Linux Luminarium(pwn.college)
Hello Hackers
本模块将教你与命令行交互的基础知识!命令行允许您执行命令。当您启动终端时,它将执行命令行 “shell”,如下所示:
hacker@dojo:~$
这称为 “prompt”,它会提示您输入命令。让我们来看看这里发生了什么:
提示中的hacker
是当前用户的用户名。在 pwn.college网站的虚拟机DOJO环境中,默认用户就是“hacker”。
在上面的示例中,提示符的 dojo
部分是 shell 所在的机器的主机名(例如,如果您是每天处理许多机器的系统管理员,则此提醒可能很有用)。在上面的例子中,主机名是 dojo
,但在 pwn.college 中,它将从你正在尝试的挑战的名称派生出来。
我们可以看看自己的虚拟机,你可以看到自己的主机名接在你的用户名后边
我们稍后会介绍 ~
的含义 :-)
提示符末尾的 $
表示hacker
不是管理用户。在 pwn.college 的后面的模块中,当你学会使用漏洞利用程序成为管理用户时,你会看到这里会通过回显 #
而不是 $
来表示你的权限,然后你就会知道你成功了!
Intro to Commands 命令介绍
在这个挑战中,您将调用您的第一个命令!当您键入命令并按 Enter 键时,将调用该命令,如下所示:
hacker@dojo:~$ whoami
hacker
hacker@dojo:~$
这里,用户执行了 whoami
命令,该命令只是将用户名 (hacker
) 打印到终端。当命令终止时,shell 将再次显示提示符,为下一个命令做好准备。
在这一关中,调用 hello
命令以获取flag!请记住:Linux 中的命令区分大小写:hello
与 HELLO
不同。
查看解析
hello
我们直接输入"hello"即可调用"hello"命令
Intro to Argument 参数介绍
让我们尝试更复杂的方法:带有参数的命令,这就是我们所说的传递给命令的附加数据。当您键入一行文本并按 Enter 键时,shell 实际上会将您的输入解析为命令及其参数。
第一个单词是命令,后面的单词是参数。示例如下:
hacker@dojo:~$ echo Hello
Hello
hacker@dojo:~$
在本例中,命令是 echo
,参数是 Hello
。echo
是一个简单的命令,它将其所有参数都“回显”到终端上,就像您在上面的会话中看到的那样。
让我们看看具有多个参数的 echo
:
hacker@dojo:~$ echo Hello Hackers!
Hello Hackers!
hacker@dojo:~$
在本例中,命令是 echo
,而 Hello
和 Hackers!
是 echo
的两个参数。很简单对吧!
在此挑战中,要获取flag,您必须运行带有一个 hackers
参数的hello
命令(不是 echo
命令)。现在就试试吧!
查看解析
hello hackers
我们在"hello"命令后接上"hackers"参数即可
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 文件系统有大量的目录和大量的文件。您可以使用 cd
(change 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 directory
(cwd
) 。 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的副本写入您在命令行上的参数指定的文件,并具有以下条件:
- 您的参数必须是绝对路径。
- 该路径必须位于您的主目录中。
- 在扩展之前,您的参数必须为三个字符或更少。
查看解析
/challenge/run ~/a
按照题目提示"run"程序能够将flag的副本写入参数指定的文件,我们使用"~"代替绝对路径"/home/hacker"
Comprehending Commands 理解命令
cat: not the pet, but the command cat:不是宠物而是命令
最关键的 Linux 命令之一是 cat
。cat
最常用于读出文件,如下所示:
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”的文本行
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"命令删除文件
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 史诗级的文件系统任务
凭借您对 cd
、ls
和 cat
的了解,我们已经准备好玩一个小游戏了!
我们将从 /
开始。如下所示:
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!在这里,您将使用 ls
和 cat
来跟踪我的线索并找到它!以下是它的工作原理:
- 你的第一个线索在
/
中。向那边走。 - 用
ls
环顾四周。将会有一个名为 HINT 或 CLUE 或类似名称的文件! cat
那个文件来读线索!- 根据线索所说的内容,前往下一个目录(或者不要!
- 循线索到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 学习复杂用法
虽然使用大多数命令很简单,但某些命令的使用可能会变得相当复杂。例如,像 sed
和 awk
这样的命令的参数(我们现在先不讨论这些)是用深奥的编程语言编写的整个程序!在 cd
和 awk
之间的某些地方是将参数转换为其参数的命令......
这听起来很疯狂,但您已经在 基本命令 中的 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
命令。man
是 manual
的缩写,它将显示(如果可用的话)您作为参数传递的命令的手册。例如,假设我们想了解 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
的参数中使用 ?
字符来替代 c
和 l
!达成条件后,运行 /challenge/run
以获取flag!
查看解析
cd /?ha??enge
./run
我们使用通配符"?"将"/?ha??enge"匹配为"/challenge"
Matching with [] 与[]匹配
接下来,我们将介绍 []。
方括号实质上是 ?
的有限形式,因为 []
不是匹配任何字符,而是在方括号内指定的某些潜在字符子集的通配符。例如,[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
在这里试试吧!我们在 /challenge/files
中放置了一堆文件。将工作目录更改为 /challenge/files
,并使用一个参数运行 /challenge/run
,使用[]
将 file_b
、file_a
、file_s
和 file_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_b
、file_a
、file_s
和 file_h
文件的绝对路径括起来!
查看解析
/challenge/run /challenge/files/file_[bash]
我们使用通配符"[]"将"file_[bash]"匹配为`file_b`、`file_a`、`file_s`、`file_h`四个文件
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
并对所有不以 p
、w
或 n
开头的文件运行 /challenge/run
!
注意:当 !
字符不是 []
glob 的第一个字符时,它在 bash 中具有不同的特殊含义,因此如果事情不再有意义,请记住这一点!^
没有这个问题,但也不兼容旧的 shell
查看解析
cd /challenge/files
../run [!pwn]*
我们使用通配符"[!]"和"*"将"[!pwn]*"匹配为开头不为`p`、`w`、`n`的文件
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
的输出之外,您当然还可以重定向任何命令的输出(比如print
、cat
等 )。在这个关卡中,/challenge/run
将再次给你一个flag,但前提是你将其输出重定向到文件 myflag
中。当然,您的flag最终会重定向出现在 myflag
文件中!
你会注意到尽管你重定向了 stdout ,/challenge/run
仍然会有输出打印到你的终端。那是因为它通过标准错误(stderr)来传达其指令和反馈,并且只在stdout上打印flag!
stderr
是标准错误流(Standard Error)的缩写,用于输出错误消息或诊断信息。
查看解析
/challenge/run > myflag
cat myflag
我们使用">"将"/challenge/run"输出流写入"COLLEGE"文件中
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
)。让我们把这个放在一起吧!
在这个更复杂的关卡时,我们希望您:
- 将
/challenge/run
的输出重定向到/tmp/data.txt
。 - 这将产生 10 万行文本,其中之一是
/tmp/data.txt
中的flag。 - 使用
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
Grepping errors grep错误
您知道如何将错误输出重定向到文件,并且知道如何通过管道将输出传输到另一个程序。但是,如果您想直接 grep 错误输出怎么办?
>
运算符将给定的文件描述符重定向到文件,并且您使用了 2>
重定向 fd 2,这是标准错误。管道符 |
仅将标准输出重定向到另一个程序,并且不存在管道符的 2|
形式!它只能重定向标准输出(FD 1)。
shell 有一个 >&
运算符,它将一个文件描述符重定向到另一个文件描述符。这意味着我们可以有一个两步过程来grep修复错误:首先,我们将标准错误重定向到标准输出(2>&
1),然后将现在组合的stderr和stdout作为正常管道(|
)!
现在就试试吧!与上一关一样,此关卡将用输出为难您,但这次是标准错误输出。解决它并找到flag!
查看解析
/challenge/run 2>&1 | grep pwn.college
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!
现在,你试试吧!在这关中 /challenge/pwn
必须通过管道传输到 /challenge/college
,但你需要拦截数据才能看到 pwn
需要你做什么!
查看解析
/challenge/pwn | tee output | /challenge/college
cat output
通过使用"tee"将"pwn"程序的输出流复制到"output"
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
在上面,发生了以下事件序列:
bash
启动了rev
命令,将命名管道(可能是/dev/fd/63
,我们可以使用命令echo >(rev)
看看命名管道)挂接到rev
的标准输入bash
启动了tee
命令,将管道挂接到其标准输入,并将tee
的第一个参数替换为/dev/fd/63
。tee
甚至从未看到过参数>(rev);
Shell 在推出tee
之前替换了它bash
使用echo
内置函数将HACK
打印到tee
的标准输入中tee
读取HACK
,将其写入标准输出,然后将其写入/dev/fd/63
(连接到rev
的 stdin管道)rev
从其标准输入中读取HACK
,将其反转,并将KCAH
写入标准输出
个人理解是将输出流写入文件是直接使用>
,而使用>()
是将输出流导到下一个程序输入流的管道中
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
:该程序会在 stdout 和 stderr 上生成数据/challenge/the
: 您必须将Hack
的 stderr 重定向到此程序/challenge/planet
: 您必须将Hack
的 stdout 重定向到此程序
Go get the flag! 去拿flag吧!
查看解析
/challenge/hack > >(/challenge/planet) 2> >(/challenge/the)
这里试着解释一下为什么
/challenge/hack >(/challenge/planet) 2>(/challenge/the)
不行
使用 `>(command)` 时,它会创建一个可用于重定向的文件描述符,而不是用作参数
`>`用于提取出输出流,`>()`用于指定程序输入流的管道
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
。注意:变量的名称和值都区分大小写!PWN
与 pwn
不同,COLLEGE
与 College
不同
查看解析
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!
在这个挑战中,你的工作是使用 read
将 PWN
变量设置为值 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
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 -ef
和 ps 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 -ef
和 ps aux
之间有许多共同点:都显示用户(USER
列)、PID、TTY、进程的开始时间 (STIME
/START
)、总利用的 CPU 时间 (TIME
) 和命令 (CMD
/COMMAND
)。ps -ef
还输出父进程 ID (PPID
),就是与进程启动相关的进程的 PID,而 ps aux
输出进程正在使用的系统 CPU 和内存总数的百分比。另外,还有许多其他内容我们现在不会讨论。
无论如何!让我们练习一下。在这个关卡中,我再次将 /challenge/run
重命名为随机文件名,这一次你不能ls
/challenge
目录!但是我也启动了它,所以可以在正在运行的进程列表中找到它,找出文件名,然后直接为flag重新启动它!祝你好运!
注意:ps -ef
和 ps aux
都将命令列表截断到终端的宽度(这就是为什么上面的示例在屏幕右侧排列得如此漂亮。如果您无法读取进程的整个路径,则可能需要放大终端(或将输出重定向到某个位置以避免这种截断行为)!
查看解析
ps -ef
使用"ps -ef"命令查看正在运行的进程,其中包含了/challenge/run的程序名< /challenge/read_me
Killing Processes kill进程
您已经启动了流程,查看了流程,现在您将学习如何终止流程!在 Linux 中,这是使用名称激进的 kill
命令完成的。使用默认选项(这就是我们将在本关卡中介绍的全部内容),kill
将终止一个进程,使其有机会在不再存在之前将其事务整理好。
假设您在另一个终端中启动了一个讨厌的 sleep
进程(sleep
是一个程序,它只是在命令行上指定的秒数内挂起,在本例中为 1337 秒),如下所示:
hacker@dojo:~$ sleep 1337
我们如何摆脱它?您可以通过将进程标识符(来自 ps
的 PID
)作为参数传递来使用 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
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
,进程已暂停。bash
的 STAT
列中的 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 $?