Shell 变量


Linux 启明道场.

Linux 命令行界面其实是一门功能丰富的编程语言,你可以用它编写真正的程序! 由于命令行界面通常被称为“shell”,用这门语言写成的程序也就叫作“shell 脚本”。 使用命令行时,你实际上就是在逐行编写 shell 脚本!

和大多数编程语言一样,shell 也支持变量。 本模块将带你熟悉如何设置、打印和使用这些变量!


  • Linux 文档项目中有一节专门介绍 shell 变量。

先从打印变量开始。 /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 变量,完成这一关!

除了读取变量中保存的值,你自然也可以给变量赋值。 和许多其他语言一样,这需要使用 =。 要把变量 VAR 设为 1337,可以这样写:

hacker@dojo:~$ VAR=1337

注意,= 两边没有空格! 如果加了空格(例如 VAR = 1337),shell 就不会把它识别为变量赋值,而是会尝试运行 VAR 命令(这条命令并不存在)。

还要注意,这里用的是 VAR,而不是 $VAR:$ 只有在访问变量时,才需要加在变量名前。 用 shell 的术语来说,加上 $ 会触发所谓的变量展开。出人意料的是,这也是很多潜在漏洞的来源(如果你对此感兴趣,等熟悉命令行后,可以去看看 Art of the Shell 道场!)。

设置变量后,就可以用之前学过的方法访问它,例如:

hacker@dojo:~$ echo $VAR
1337

要完成本关,你必须把 PWN 变量设为 COLLEGE。 请注意:变量名和变量值都区分大小写! PWN 与 pwn 不同,COLLEGE 与 College 也不同。

本关将介绍引号的用法。 空格在 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。 祝你好运!

默认情况下,你在 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 的提示符。它是一个精简的 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 继承它)。 祝你好运!

在 bash 中,访问变量有多种方法。 echo 只是其中一种,本关将再介绍一种。

试试 env 命令:它会打印 shell 中设置的所有已导出变量,你可以在输出中寻找 FLAG 变量!

使用 shell 时,你经常会想把某条命令的输出保存到变量中。 好在 shell 提供了命令替换,让这件事变得非常简单! 看下面的例子:

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`。

我们先从读取用户(也就是你)的输入开始。 这需要使用名副其实的内建命令 read,它会把输入读入变量!

下面的例子使用了 -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: 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。 祝你好运!

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 变量!


30天排行榜:

此排行榜反映本模块在道场上线后各挑战的解出情况。

排名 黑客 徽章 得分
暂无解题排行数据 —— 注册账号并完成挑战即可登上榜单!