Skip to content

输入和输出

输入控制

我们可能会从我们发送的请求和执行的命令中得到结果,我们必须手动决定如何继续。另一个例子是我们在为不同场景设计的脚本中定义了几个函数。我们必须在手动检查后并根据结果来决定应该执行哪些。也很有可能不允许执行特定的扫描或活动。因此,我们需要熟悉如何获得一个运行脚本来等待我们的指令。如果我们CIDR.sh再次查看我们的脚本,我们会看到我们添加了这样一个调用来决定进一步的步骤。

CIDR.sh

# Available options
<SNIP>
echo -e "Additional options available:"
echo -e "\t1) Identify the corresponding network range of target domain."
echo -e "\t2) Ping discovered hosts."
echo -e "\t3) All checks."
echo -e "\t*) Exit.\n"

read -p "Select your option: " opt

case $opt in
    "1") network_range ;;
    "2") ping_host ;;
    "3") network_range && ping_host ;;
    "*") exit 0 ;;
esac

第一echo行用作我们可用选项的显示菜单。使用该read命令,将Select your option:显示带有“”的行,并且附加选项-p确保我们的输入保持在同一行。我们的输入存储在变量opt中,然后我们用它来执行带有case语句的相应函数,我们稍后会看到。根据我们输入的数字,case语句确定执行哪些函数。

输出控制

我们已经了解了Linux Fundamentals模块中输出的输出重定向。然而,重定向的问题是我们没有从相应的命令中获得任何输出。它将被重定向到适当的文件。如果我们的脚本以后变得更复杂,它们可能需要更多的时间,而不仅仅是几秒钟。为了避免无所事事地等待脚本的结果,我们可以使用tee实用程序。它确保我们立即看到我们得到的结果,并将它们存储在相应的文件中。在我们的CIDR.sh脚本中,我们以不同的方式两次使用了这个实用程序。

CIDR.sh

<SNIP>

# Identify Network range for the specified IP address(es)
function network_range {
    for ip in $ipaddr
    do
        netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
        cidr=$(whois $ip | grep "CIDR" | awk '{print $2}')
        cidr_ips=$(prips $cidr)
        echo -e "\nNetRange for $ip:"
        echo -e "$netrange"
    done
}

<SNIP>

# Identify IP address of the specified domain
hosts=$(host $domain | grep "has address" | cut -d" " -f4 | tee discovered_hosts.txt)

<SNIP>

使用时tee,我们将接收到的输出传输并使用管道(|)将其转发到tee. “ -a--append”参数确保指定的文件不被覆盖,而是用新的结果进行补充。同时,它向我们展示了结果以及如何在文件中找到它们。

darkinga@htb[/htb]$ cat discovered_hosts.txt CIDR.txt

165.22.119.202
NetRange:       165.22.0.0 - 165.22.255.255
CIDR:           165.22.0.0/16

Flow Control - 循环(Loops)

控制我们的脚本流程是必不可少的。我们已经了解了if-else条件,这也是流量控制的一部分。毕竟,我们希望我们的脚本能够快速高效地工作,为此,我们可以使用其他组件来提高效率并允许无错误处理。每个控制结构都是 abranch或 a loop。布尔值的逻辑表达式通常控制控制结构的执行。这些控制结构包括:

  • 分支机构:

    • If-Else条件
    • Case声明
    • 循环:

    • For循环

    • While循环
    • Until循环

For 循环

让我们从For循环开始。循环在For每次传递时执行一个参数,shell 从列表中获取,从增量计算,或者从另一个数据源获取。只要找到相应的数据,for 循环就会运行。这种类型的循环可以以不同的方式构建和定义。例如,当我们需要处理数组中的许多不同值时,通常会使用 for 循环。这可用于扫描不同的主机或端口。我们还可以使用它对已知端口及其服务执行特定命令,以加快我们的枚举过程。其语法如下:

语法 - 示例

for $variable in 1 2 3 4
do
    echo $variable
done
for $variable in file1 file2 file3
do
    echo $variable
done
for ip in "10.10.10.170 10.10.10.174 10.10.10.175"
do
    ping -c 1 $ip
done

当然,我们也可以将这些命令写在一行中。这样的命令看起来像这样:

$ for ip in 10.10.10.170 10.10.10.174;do ping -c 1 $ip;done

PING 10.10.10.170 (10.10.10.170): 56 data bytes
64 bytes from 10.10.10.170: icmp_seq=0 ttl=63 time=42.106 ms

--- 10.10.10.170 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 42.106/42.106/42.106/0.000 ms
PING 10.10.10.174 (10.10.10.174): 56 data bytes
64 bytes from 10.10.10.174: icmp_seq=0 ttl=63 time=45.700 ms

--- 10.10.10.174 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 45.700/45.700/45.700/0.000 ms

让我们再看看我们的CIDR.sh脚本。我们在脚本中添加了几个 for 循环,但让我们坚持这个小代码部分。

CIDR.sh

<SNIP>

# Identify Network range for the specified IP address(es)
function network_range {
    for ip in $ipaddr
    do
        netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
        cidr=$(whois $ip | grep "CIDR" | awk '{print $2}')
        cidr_ips=$(prips $cidr)
        echo -e "\nNetRange for $ip:"
        echo -e "$netrange"
    done
}

<SNIP>

与前面的示例一样,对于数组 " ipaddr" 中的每个 IP 地址,我们发出一个 " whois" 请求,其输出被过滤为 " NetRange" 和 " CIDR。" 这有助于我们确定目标所在的地址范围。我们可以使用这些信息在渗透测试期间搜索其他主机,if approved by the client. 我们收到的结果会相应显示并存储在文件“ CIDR.txt.”中。


While 循环

循环在while概念上很简单,遵循以下原则:

  • 只要满足条件(true),就会执行语句。

我们还可以组合循环并将它们的执行与不同的值合并。需要注意的是,多个循环相互过度组合会使代码非常不清楚,并导致难以查找和跟踪的错误。这样的组合可以看起来像我们的CIDR.sh脚本。

CIDR.sh

<SNIP>
        stat=1
        while [ $stat -eq 1 ]
        do
            ping -c 2 $host > /dev/null 2>&1
            if [ $? -eq 0 ]
            then
                echo "$host is up."
                ((stat--))
                ((hosts_up++))
                ((hosts_total++))
            else
                echo "$host is down."
                ((stat--))
                ((hosts_total++))
            fi
        done
<SNIP>

while循环也适用于if-else. 当一个while循环必须停止执行它包含的命令时,它需要某种计数器来定位自己。否则,这会导致无限循环。这样的计数器可以是我们用特定值或布尔值声明的变量。While当布尔值为“ True”时循环运行。除了计数器之外,我们还可以使用命令“ ” break,它会在到达该命令时中断循环,如下例所示:

WhileBreaker.sh

#!/bin/bash

counter=0

while [ $counter -lt 10 ]
do
  # Increase $counter by 1
  ((counter++))
  echo "Counter: $counter"

  if [ $counter == 2 ]
  then
    continue
  elif [ $counter == 4 ]
  then
    break
  fi
done

WhileBreaker.sh

darkinga@htb[/htb]$ ./WhileBreaker.sh

Counter: 1
Counter: 2
Counter: 3
Counter: 4

Until Loops

还有until循环,比较少见。尽管如此,until循环的工作方式与循环完全相同while,但不同之处在于:

  • until只要特定条件为 ,就会执行循环内的代码false

另一种方法是让循环运行直到达到所需的值。" until" 循环非常适合这种情况。这种循环的工作方式与 " while" 循环类似,但如前所述,不同之处在于它一直运行到布尔值为 " False。"

Until.sh

#!/bin/bash

counter=0

until [ $counter -eq 10 ]
do
  # Increase $counter by 1
  ((counter++))
  echo "Counter: $counter"
done

Until.sh

$ ./Until.sh

Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
Counter: 6
Counter: 7
Counter: 8
Counter: 9
Counter: 10

练习脚本

#!/bin/bash

# Decrypt function
function decrypt {
    MzSaas7k=$(echo $hash | sed 's/988sn1/83unasa/g')
    Mzns7293sk=$(echo $MzSaas7k | sed 's/4d298d/9999/g')
    MzSaas7k=$(echo $Mzns7293sk | sed 's/3i8dqos82/873h4d/g')
    Mzns7293sk=$(echo $MzSaas7k | sed 's/4n9Ls/20X/g')
    MzSaas7k=$(echo $Mzns7293sk | sed 's/912oijs01/i7gg/g')
    Mzns7293sk=$(echo $MzSaas7k | sed 's/k32jx0aa/n391s/g')
    MzSaas7k=$(echo $Mzns7293sk | sed 's/nI72n/YzF1/g')
    Mzns7293sk=$(echo $MzSaas7k | sed 's/82ns71n/2d49/g')
    MzSaas7k=$(echo $Mzns7293sk | sed 's/JGcms1a/zIm12/g')
    Mzns7293sk=$(echo $MzSaas7k | sed 's/MS9/4SIs/g')
    MzSaas7k=$(echo $Mzns7293sk | sed 's/Ymxj00Ims/Uso18/g')
    Mzns7293sk=$(echo $MzSaas7k | sed 's/sSi8Lm/Mit/g')
    MzSaas7k=$(echo $Mzns7293sk | sed 's/9su2n/43n92ka/g')
    Mzns7293sk=$(echo $MzSaas7k | sed 's/ggf3iunds/dn3i8/g')
    MzSaas7k=$(echo $Mzns7293sk | sed 's/uBz/TT0K/g')

    flag=$(echo $MzSaas7k | base64 -d | openssl enc -aes-128-cbc -a -d -salt -pass pass:$salt)
}

# Variables
var="9M"
salt=""
hash="VTJGc2RHVmtYMTl2ZnYyNTdUeERVRnBtQWVGNmFWWVUySG1wTXNmRi9rQT0K"

# Base64 Encoding Example:
#        $ echo "Some Text" | base64

# <- For-Loop here

# Check if $salt is empty
if [[ ! -z "$salt" ]]
then
    decrypt
    echo $flag
else
    exit 1
fi

创建一个“for”循环,在“base64”中对变量“var”进行 28 次编码。第 28 个哈希中的字符数是必须分配给“salt”变量的值。


Flow Control - Branches(分支)

正如我们已经看到的,流控制中的分支包括if-elsecase语句。我们已经详细讨论了这些if-else语句,并且知道它是如何工作的。现在我们将仔细研究案例陈述。

案例陈述

Case语句在其他语言中也称为switch-case语句,例如 C/C++ 和 C#。if-elseswitch-case之间的主要区别在于if-else构造允许我们检查任何布尔表达式,而switch-case始终只将变量与确切值进行比较。因此,不允许使用与 if-else 相同的条件,例如“大于” 。switch-case语句的语法如下所示:

语法 - Switch-Case

case <expression> in
    pattern_1 ) statements ;;
    pattern_2 ) statements ;;
    pattern_3 ) statements ;;
esac

switch-case 的定义以 case 开头,后跟作为表达式的变量或值,然后在模式中进行比较。如果变量或值与表达式匹配,则语句在括号后执行并以双分号 ( ;;) 结束。

在我们的CIDR.sh脚本中,我们使用了这样的case语句。在这里,我们定义了分配给脚本的四个不同选项,在我们做出决定后它应该如何进行。

CIDR.sh

<SNIP>
# Available options
echo -e "Additional options available:"
echo -e "\t1) Identify the corresponding network range of target domain."
echo -e "\t2) Ping discovered hosts."
echo -e "\t3) All checks."
echo -e "\t*) Exit.\n"

read -p "Select your option: " opt

case $opt in
    "1") network_range ;;
    "2") ping_host ;;
    "3") network_range && ping_host ;;
    "*") exit 0 ;;
esac
<SNIP>

使用前两个选项,此脚本执行我们之前定义的不同功能。使用第三个选项,两个函数都被执行,而使用任何其他选项,脚本将被终止。

bash