近期解题 2024.2.16
文章目录
春秋杯 - upx2023
程序放进 Exeinfo,其实听名字就知道有壳,但是这个壳改过。
010 Editor 里看一眼,这个壳改得挺没品的,upx 段标识改回大写后,顺利脱壳。
进入 IDA 分析:
1int __fastcall main(int argc, const char **argv, const char **envp)
2{
3 std::ostream *v3; // rax
4 char *v4; // rax
5 int v6[44]; // [rsp+20h] [rbp-60h] BYREF
6 char v7[16]; // [rsp+D0h] [rbp+50h] BYREF
7 char v8[16]; // [rsp+E0h] [rbp+60h] BYREF
8 char v9[20]; // [rsp+F0h] [rbp+70h] BYREF
9 int v10; // [rsp+104h] [rbp+84h]
10 unsigned int Seed; // [rsp+108h] [rbp+88h]
11 int i; // [rsp+10Ch] [rbp+8Ch]
12
13 _main();
14 Seed = time(0i64);
15 srand(Seed);
16 std::string::string(v7);
17 std::operator<<<std::char_traits<char>>(&std::cout, Str);
18 std::operator>><char>(&std::cin, v7);
19 std::string::string(v9, v7);
20 change(v8, v9);
21 std::string::operator=(v7, v8);
22 std::string::~string(v8);
23 std::string::~string(v9);
24 if ( std::string::length(v7) != 42 )
25 {
26 v3 = std::operator<<<std::char_traits<char>>(&std::cout, "len error");
27 std::endl<char,std::char_traits<char>>(v3);
28 exit(0);
29 }
30 qmemcpy(v6, &unk_46A020, 0xA8ui64);
31 for ( i = 0; i <= 41; ++i )
32 {
33 v10 = rand() % 255;
34 v4 = std::string::operator[](v7, i);
35 if ( (v10 ^ *v4) != v6[i] )
36 exit(0);
37 }
38 std::string::~string(v7);
39 return 0;
40}
其中 change()
函数如下:
1std::string *__fastcall change(std::string *a1, std::string *a2)
2{
3 __int64 v2; // rdi
4 void *v3; // rsp
5 _BYTE *v4; // rax
6 _BYTE v6[32]; // [rsp+0h] [rbp-80h] BYREF
7 __int64 v7[7]; // [rsp+20h] [rbp-60h] BYREF
8 char v8; // [rsp+5Fh] [rbp-21h] BYREF
9 __int64 *v9; // [rsp+60h] [rbp-20h]
10 __int64 v10; // [rsp+68h] [rbp-18h]
11 __int64 v11; // [rsp+70h] [rbp-10h]
12 int v12; // [rsp+7Ch] [rbp-4h]
13 int v13; // [rsp+80h] [rbp+0h]
14 int n; // [rsp+84h] [rbp+4h]
15 int m; // [rsp+88h] [rbp+8h]
16 int k; // [rsp+8Ch] [rbp+Ch]
17 char v17; // [rsp+93h] [rbp+13h]
18 int v18; // [rsp+94h] [rbp+14h]
19 int j; // [rsp+98h] [rbp+18h]
20 int i; // [rsp+9Ch] [rbp+1Ch]
21
22 v7[5] = v6;
23 v13 = 3;
24 std::allocator<char>::allocator(&v6[95]);
25 std::string::string(a1, &unk_47F000, &v8);
26 std::allocator<char>::~allocator(&v8);
27 v12 = std::string::length(a2);
28 v11 = v12 - 1i64;
29 v7[1] = 0i64;
30 v2 = v12;
31 v10 = v13 - 1i64;
32 v7[0] = v11;
33 v7[2] = v12;
34 v7[3] = 0i64;
35 v3 = alloca(16 * ((v12 * v13 + 15) >> 4));
36 v9 = v7;
37 for ( i = 0; i < v13; ++i )
38 {
39 for ( j = 0; j < v12; ++j )
40 *(v9 + j + v2 * i) = 10;
41 }
42 v18 = 0;
43 v17 = 0;
44 for ( k = 0; k < v12; ++k )
45 {
46 if ( !v18 || v13 - 1 == v18 )
47 v17 ^= 1u;
48 v4 = std::string::operator[](a2, k);
49 *(v9 + k + v2 * v18) = *v4;
50 if ( v17 )
51 ++v18;
52 else
53 --v18;
54 }
55 for ( m = 0; m < v13; ++m )
56 {
57 for ( n = 0; n < v12; ++n )
58 {
59 if ( *(v9 + n + v2 * m) != 10 )
60 std::string::operator+=(a1);
61 }
62 }
63 return a1;
64}
打眼一看,这个函数除了该顺序之外没干别的事,遂打断点动调找调整过的顺序顺序。输入 "flag{1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ}",得到如下顺序:
1>>> ['flag{1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ}'.index(i) for i in 'f{48BFJNRVZlg13579ACEGIKMOQSUWY}a260DHLPTX']
2[0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 2, 6, 10, 14, 18, 22, 26, 30, 34, 38]
随后处理 rand() % 255
,我试图爆破,但是误将调整后的 rand()
顺序当作调整前的 rand()
顺序而没能成功得到 Seed
。赛后我修改了顺序得到 Seed
,可以说是最遗憾的一集了。
程序编译好的时间对应的时间戳是 1685762995
,因此 Seed
一定在这之前出现。flag 的前几个字符 “flag{” 已知,可以计算出随机数序列中第 0、11、32、12、1 的值分别为 0x6F、0xAA、0x9B、0x2、0x18。用下面的脚本爆破:
1#include <iostream>
2#include <cstdlib>
3#include <ctime>
4
5using namespace std;
6
7int main()
8{
9 int num[32] = {0};
10 for(unsigned int seed = 1662973302; seed < 1685762995; seed++)
11 {
12 for(int i = 0; i <= 32; i++)
13 {
14 num[i] = rand() % 255;
15 }
16 if(num[0] == 0x6F && num[1] == 0x18 && num[11] == 0xAA && num[12] == 0x2 && num[32] == 0x9B)
17 {
18 cout << seed << endl;
19 }
20 }
21}
22
23// output: 1682145110
得到了种子就可以写解密脚本了:
1#include <iostream>
2#include <cstdio>
3#include <cstdlib>
4
5using namespace std;
6
7int main()
8{
9 int seq[42] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 2, 6, 10, 14, 18, 22, 26, 30, 34, 38};
10 int cip[42] =
11 {
12 0x09, 0x63, 0xD9,
13 0xF6, 0x58, 0xDD, 0x3F, 0x4C,
14 0x0F, 0x0B, 0x98, 0xC6, 0x65,
15 0x21, 0x41, 0xED, 0xC4, 0x0B,
16 0x3A, 0x7B, 0xE5, 0x75, 0x5D,
17 0xA9, 0x31, 0x41, 0xD7, 0x52,
18 0x6C, 0x0A, 0xFA, 0xFD, 0xFA,
19 0x84, 0xDB, 0x89, 0xCD, 0x7E,
20 0x27, 0x85, 0x13, 0x08
21 };
22 int seed = 1682145110;
23 srand(seed);
24 int flag[42] = {0};
25
26 for(int i = 0;i < 42; i++)
27 {
28 flag[i] = (rand() % 255) ^ cip[i];
29 }
30 for(int j = 0;j < 42; j++)
31 {
32 cout << flag[seq[j]];
33 }
34}
flag{0305f8f2-14b6-fg7b-bc7a-010299c881e1}
攻防世界 - handcrafted-pyc
我在第七周的解题记录里完成了这道题目的一部分,但是并没有完全解出来,今天填坑。
题目附件是 .py
的源代码:
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4import marshal, zlib, base64
5
6exec(marshal.loads(zlib.decompress(base64.b64decode('eJyNVktv00AQXm/eL0igiaFA01IO4cIVCUGFBBJwqRAckLhEIQmtRfPwI0QIeio/hRO/hJ/CiStH2M/prj07diGRP43Hs9+MZ2fWMxbnP6mux+oK9xVMHPFViLdCTB0xkeKDFEFfTIU4E8KZq8dCvB4UlN3hGEsdddXU9QTLv1eFiGKGM4cKUgsFCNLFH7dFrS9poayFYmIZm1b0gyqxMOwJaU3r6xs9sW1ooakXuRv+un7Q0sIlLVzOCZq/XtsK2oTSYaZlStogXi1HV0iazoN2CV2HZeXqRQ54TlJRb7FUlKyUatISsdzo+P7UU1Gb1POdMruckepGwk9tIXQTftz2yBaT5JQovWvpSa6poJPuqgao+b9l5Aj/R+mLQIP4f6Q8Vb3g/5TB/TJxWGdZr9EQrmn99fwKtTvAZGU7wzS7GNpZpDm2JgCrr8wrmPoo54UqGampFIeS9ojXjc4E2yI06bq/4DRoUAc0nVnng4k6p7Ks0+j/S8z9V+NZ5dhmrJUM/y7JTJeRtnJ2TSYJvsFq3CQt/vnfqmQXt5KlpuRcIvDAmhnn2E0t9BJ3SvB/SfLWhuOWNiNVZ+h28g4wlwUp00w95si43rZ3r6+fUIEdgOZbQAsyFRRvBR6dla8KCzRdslar7WS+a5HFb39peIAmG7uZTHVm17Czxju4m6bayz8e7J40DzqM0jr0bmv9PmPvk6y5z57HU8wdTDHeiUJvBMAM4+0CpoAZ4BPgJeAYEAHmgAUgAHiAj4AVAGORtwd4AVgC3gEmgBBwCPgMWANOAQ8AbwBHgHuAp4D3gLuARwoGmNUizF/j4yDC5BWM1kNvvlxFA8xikRrBxHIUhutFMBlgQoshhPphGAXe/OggKqqb2cibxwuEXjUcQjccxi5eFRL1fDSbKrUhy2CMb2aLyepkegDWsBwPlrVC0/kLHmeCBQ=='))))
代码阅读上并没有什么难度,首先把一个字符串 Base64 解码,然后 zlib 解压缩,再然后得到反序列化,最后反序列化成 Python 代码并执行。
看上去肯简单对吧,但是运行一下,果然报错:
1ValueError: bad marshal data (unknown type code)
看来是反序列化那一步出了问题。我们不妨先拿到解压后的 .pyc
字节码再进行下一步分析。如下图,我们得到的 .pyc
文件缺少 magic number。
我尝试了几个不同版本 Python 的 magic number,但都会提示反编译失败。不过,尽管没有得到源代码,在使用 uncompyle6 时仍然得到了一段 Bytecode。第七周那一次周报里,我把完整的 Bytecode 贴上了。下面贴一小段简单分析一下:
1 L. 1 0 LOAD_GLOBAL 0 'chr'
2 3 LOAD_CONST 108
3 6 CALL_FUNCTION_1 1 None
4 9 LOAD_GLOBAL 0 'chr'
5 12 LOAD_CONST 108
6 15 CALL_FUNCTION_1 1 None
7 18 LOAD_GLOBAL 0 'chr'
8 21 LOAD_CONST 97
9 24 CALL_FUNCTION_1 1 None
10 27 LOAD_GLOBAL 0 'chr'
11 30 LOAD_CONST 67
12 33 CALL_FUNCTION_1 1 None
13 36 ROT_TWO
14 37 BINARY_ADD
15 38 ROT_TWO
16 39 BINARY_ADD
17 40 ROT_TWO
18 41 BINARY_ADD
程序依次将 chr(108)
、chr(108)
、chr(97)
、chr(67)
入栈,随后 ROT_TWO
将栈顶的两个字符交换顺序,BINARY_ADD
将栈顶的两个元素相加(字符与字符相加得到一个字符串)。这一段字节码执行结束后可以在栈顶得到 'Call' 这个单词。
后面的字节码和这一段类似,都是将数个字符入栈并且倒序输出。可以用以下脚本把全部字符提取并且输出(可以说是一个小小虚拟机):
1import re
2
3string = ''
4
5find = True
6buffer = ''
7
8with open(r'C:\Users\jack_gdn\Desktop\temp files\攻防世界 - handcrafted-pyc\download.txt', 'r') as file:
9 for line in file:
10 load_const = re.search(r'LOAD_CONST\s{15}(\d+)', line)
11 rot_two = re.search(r'ROT_TWO', line)
12 if load_const:
13 buffer = buffer + chr(int(load_const.group(1)))
14 find = True
15 if rot_two and find:
16 print(buffer[::-1], end = '')
17 find = False
18 buffer = ''
19
20# output: Call me a Python virtual machine! I can interpret Python bytecodes!!!hitcon{Now you can compile and run Python bytecode in your brain!}password: Wrong password... Please try again. Do not brute force. =)
hitcon{Now you can compile and run Python bytecode in your brain!}
NSSCTF - can_can_need_pxory
近期最让我绷不住的一道题
题目附件有一个文本文件和一个程序。文本文件如下:
经过唯一性处理后,print结果如下:
NDc1NTMyNTQ0NzRGNDI1OTQ2NTEzMjU0NDc0RDVBNTU0NzQxNTc0NDQzNEQ0QTUzNDczNDVBNTQ0NTRDNDI1MzQ3NEQ1OTU0NEQ0QzQyNTI0NzQ1MzM1NDUxNEU0QTUzNDY1MTMyNDQ1MzRENTI1NTQ3NTE1NzQ0NEI0RDRBNTM0ODQ1NUE0MzU5NEQ1MjU0NDczNDM0NDM1OTRFNDI1NzQ3NDUzMzU0NDU0QzQyNTI0NzQxMzM1NDRENEQ0QTUzNDY1MTU5NTQ0MzRGNDI1OTQ3MzQzMzQzNTk0RDUyNTQ0NzQ5MzM0MzU5NEQ0QTUyNDczNDM0NDQ0QjRENTI0RDQ3NTEzMzQ0NDM0RTVBNTM0NjUxNTk1NDQ1NEQ0MjVBNDc0OTMyNDM1OTRENTI1NDQ3NEQ1OTQzNTk0RDRBNTI0NzU5MzQ0NDQ1NEY0MjRENDc0NTVBNDQ0NzRGNEE1QTQ3NTk1NzQ0NDk0RTUyNTI0NzM0NUE0MzU5NEQ1MjU0NDc0OTM0NTM1OTRENEE1MjQ4NDUzNDU0NDE0RDQyNEQ0NzQ1NTk1NDQ1NEU1QTU0NDc0OTU3NDQ0MzRENEE1MzQ3MzQ1QTU0NDU0QzQyNTM0NzREMzQ0NDRENEM0MjUyNDc0OTVBNTQ1MzRGNEE1NzQ2NTEzMjU0NDE0RDUyNTc0ODQxNTc0NDRCNEY0MjU1NDc1OTU5NDM1OTRENTI1NDQ3NDUzNDUzNTk0RDRBNTM0NzREMzQ1NDUzNEU1MjRENDc0NTU5NDQ0MzRFNDI1NzQ4NDE1NzQ0NDM0RDUyNTQ0ODQ1MzQ1NDRENEM0MjUzNDc0RDU5NTQ1MzRDNDI1NTQ4NDU1QTQ0NDk0RTQyNEQ0NzU1MzM1NDQ5NEQ1QTU3NDY1MTMyNTQ0RDRFNDI1MjQ3NDk1NzQ0NDU0RDVBNTQ0NzQ1NTc0MTNEM0QzRDNE
请将解出的flag用NSSCTF{}包裹一下喵
运行程序,会输出这么一坨东西:
这个程序使用 Pyinstaller 打包,逆向得到源代码:
1print('ccccccccccccccccccccccccccccccccccc!')
2print('D0 U know C?')
3print('\n#include <NSSCTF.h>\nv01d bnssst(int FTC[], int lenggggggg) {\n int i, j, SSN;\n')
4print('This is CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC')
5print('6L+Z5piv5L2g6KaB55qEZmxhZ+WQl++8nwpOU1NDVEZ7YjVlMzlkMDktODg3Yy1hZGI0LTE4OWMtMWI0OGEwNWJmOTY2fQ==')
6A = {
7 'flag': 'NSSCTF{a81c0d5e-ec6d-2b80' }
8print('\n SSN = FTC[j];\n printf("flag");\n }\n}\nint mian() {\n int FTC[] = [ -,\n')
9print('ccccccccccccccccccc')
10print('\nb, 2, 6, 7, -, d, 5, 8, 4, -, 6, 8, 7, 4, -, f, 1, 2, 6, e, 3, a, 5, 1, 0, 6, 1, }];\n int lenggggggg = (int) sizeof(FTC) / sizeof(*FTC);\n bnsScrt(FTC, lenggggggg);\n int i;\n for (i = 0; i < lenggggggg; i++)\n printf("%d ", FTC[i]);\n remake 0;\n}\n')
11print('----------------------------------------------------')
12import base64
13flag = '************************************'
14r = ''
15for x in range(len(flag)):
16 if (x + 1) % 4 == 0:
17 res = str(ord(chr(ord(flag[x]) ^ 2421)))
18 else:
19 res = str(ord(chr(ord(flag[x]) << 6 << 7 >> 2 >> 1 ^ 92)))
20 r = r + res + ','
21
22print(base64.b64encode(base64.b16encode(base64.b32encode(r.encode('utf-8')))).decode('utf-8'))
写出逆向脚本:
1import base64
2
3c = 'NDc1NTMyNTQ0NzRGNDI1OTQ2NTEzMjU0NDc0RDVBNTU0NzQxNTc0NDQzNEQ0QTUzNDczNDVBNTQ0NTRDNDI1MzQ3NEQ1OTU0NEQ0QzQyNTI0NzQ1MzM1NDUxNEU0QTUzNDY1MTMyNDQ1MzRENTI1NTQ3NTE1NzQ0NEI0RDRBNTM0ODQ1NUE0MzU5NEQ1MjU0NDczNDM0NDM1OTRFNDI1NzQ3NDUzMzU0NDU0QzQyNTI0NzQxMzM1NDRENEQ0QTUzNDY1MTU5NTQ0MzRGNDI1OTQ3MzQzMzQzNTk0RDUyNTQ0NzQ5MzM0MzU5NEQ0QTUyNDczNDM0NDQ0QjRENTI0RDQ3NTEzMzQ0NDM0RTVBNTM0NjUxNTk1NDQ1NEQ0MjVBNDc0OTMyNDM1OTRENTI1NDQ3NEQ1OTQzNTk0RDRBNTI0NzU5MzQ0NDQ1NEY0MjRENDc0NTVBNDQ0NzRGNEE1QTQ3NTk1NzQ0NDk0RTUyNTI0NzM0NUE0MzU5NEQ1MjU0NDc0OTM0NTM1OTRENEE1MjQ4NDUzNDU0NDE0RDQyNEQ0NzQ1NTk1NDQ1NEU1QTU0NDc0OTU3NDQ0MzRENEE1MzQ3MzQ1QTU0NDU0QzQyNTM0NzREMzQ0NDRENEM0MjUyNDc0OTVBNTQ1MzRGNEE1NzQ2NTEzMjU0NDE0RDUyNTc0ODQxNTc0NDRCNEY0MjU1NDc1OTU5NDM1OTRENTI1NDQ3NDUzNDUzNTk0RDRBNTM0NzREMzQ1NDUzNEU1MjRENDc0NTU5NDQ0MzRFNDI1NzQ4NDE1NzQ0NDM0RDUyNTQ0ODQ1MzQ1NDRENEM0MjUzNDc0RDU5NTQ1MzRDNDI1NTQ4NDU1QTQ0NDk0RTQyNEQ0NzU1MzM1NDQ5NEQ1QTU3NDY1MTMyNTQ0RDRFNDI1MjQ3NDk1NzQ0NDU0RDVBNTQ0NzQ1NTc0MTNEM0QzRDNE'
4
5raw = base64.b32decode(base64.b16decode(base64.b64decode(c.encode('utf-8')))).decode('utf-8')
6
7flag = raw[:-1].split(',')
8
9for x in range(len(flag)):
10 if (x + 1) % 4 == 0:
11 print(chr(int(flag[x]) ^ 2421), end = '')
12 else:
13 print(chr((int(flag[x]) ^ 92) << 1 << 2 >> 7 >> 6), end = '')
14
15# output: 64nys02?-itcs-vory-lunn'y19zycyz087n
我的进度到这里就结束了,后来看 WP,要想得到正确的 flag 还有下面这一步
1>>> "".join([chr(ord('64nys02?-itcs-vory-lunn\'y19zycyz087n'[i]) ^ 0xA) if (i + 1) % 4 == 0 else '64nys02?-itcs-vory-lunn\'y19zycyz087n'[i] for i in range(len('64nys02?-itcs-vory-lunn\'y19zycyz087n'))])
2'64nss025-itis-very-funn-y19pycyp087d'
最遗憾的一集,比春秋杯爆破 Seed 还遗憾
攻防世界 - mfc逆向
程序加了 VMP 壳。
试图手撕,未果;试图使用工具脱壳,未果。遂不脱壳。
程序提示 "Flag就在控件里",以及窗口中间使用了文本框控件。使用 Spy++ 查看窗体句柄。
得到句柄 0xC208A。随后使用 xspy 分析窗口。
可以看到有一个自定义消息 0x0464 会触发地址为 002170 的函数。wparam
和 lparam
不详就随便写一个试试。使用以下脚本尝试发送这个消息:
1#include <Windows.h>
2
3int main()
4{
5 HWND flag = HWND(0xC208A);
6 SendMessage(flag, 0x464, 114514, 1919810);
7}
发现窗口发生变化。
似乎有什么 DES 加密的地方,但是往后我就没辙了。看 WP 知道,"{I am a Des key}" 是 DES 加密的密钥,密文是这个窗口的类 "944c8d100f82f0c18b682f63e4dbaa207a2f1e72581c2f1b",最终得到 flag
thIs_Is_real_kEy_hahaaa
但是我在尝试 DES 解密这一步里失败了,因为 DES 要求密钥为 8 字节,但是题目所给字符串有 16 字节,3DES 对偏移量有要求但是题目中并未给出。
西湖论剑 - easy_table
编程题,比赛结束前一个半小时开始看这道题,比赛结束后四个小时拿到 flag(当然中间还干别的事去了)而且应该是正确的,至少使用样例数据运行的结果是正确的。这个过程学到了相当多的东西,包括从头开始学了 pandas 模块、学习了 re 模块以及 Python 里其他巧妙的但我以前不知道的模块、函数和语法。由于一开始没有完全读懂题,并且我是第一次使用 pandas 模块,这个脚本有相当多可以优化的地方。题目给出待处理的数据总共有 10000 组,我的电脑运行下面的脚本需要亖分多钟:
1import pandas as pd
2import re
3from datetime import datetime
4import hashlib
5
6
7def hash_calc(string):
8 md5_hash = hashlib.md5()
9 md5_hash.update(string.encode('utf-8'))
10 return md5_hash.hexdigest()
11
12
13def check_time(time_range, time_point):
14 start, end = map(lambda x: datetime.strptime(x, '%H:%M:%S'), time_range.split('~'))
15 point = datetime.strptime(time_point, '%H:%M:%S')
16 return int(start <= point <= end)
17
18
19def user_not_exist(user_id):
20 if user_id in users['账号'].values:
21 return 0
22 else:
23 return 1
24
25
26def wrong_table(table_action, user_id):
27 pattern = r"(from|update|insert into)\s+(\w+)\s"
28 matches = re.findall(pattern, table_action)
29 table = matches[0][1] # 查找表名
30
31 for index_tables, row_tables in tables.iterrows():
32 if row_tables['表名'] == table:
33 table_id = row_tables['编号'] # 查找表编号
34
35 for index_users, row_users in users.iterrows():
36 if row_users['账号'] == user_id:
37 group_id = row_users['所属权限组编号'] # 查找组编号
38 uid = row_users['编号'] # 查找用户编号
39
40 for index_permissions, row_permissions in permissions.iterrows():
41 if row_permissions['编号'] == group_id:
42 operable_tables = row_permissions['可操作表编号'].split(',') # 查找可操作表编号
43
44 if str(table_id) not in operable_tables:
45 return [1, uid, group_id, table_id]
46 else:
47 return [0]
48
49
50def wrong_operation(table_action, user_id):
51 op_lst = ['insert', 'delete', 'update', 'select']
52 pattern = r"(from|update|insert into)\s+(\w+)\s"
53 matches = re.findall(pattern, table_action)
54 table = matches[0][1] # 查找表名
55
56 for op in op_lst:
57 if op in table_action:
58 operation = op # 查找操作
59 break
60
61 for index_users, row_users in users.iterrows():
62 if row_users['账号'] == user_id:
63 group_id = row_users['所属权限组编号'] # 查找组编号
64 uid = row_users['编号'] # 查找用户编号
65
66 for index_permissions, row_permissions in permissions.iterrows():
67 if row_permissions['编号'] == group_id:
68 operable_permissions = row_permissions['可操作权限'].split(',') # 查找可操作权限
69
70 for index_tables, row_tables in tables.iterrows():
71 if row_tables['表名'] == table:
72 table_id = row_tables['编号'] # 查找表编号
73
74 if operation not in operable_permissions:
75 return [1, uid, group_id, table_id]
76 else:
77 return [0]
78
79
80def wrong_time(action_time, user_id):
81 time_point = action_time.split(' ')[1]
82 pattern = r"(from|update|insert into)\s+(\w+)\s"
83 matches = re.findall(pattern, table_action)
84 table = matches[0][1] # 查找表名
85 filt = []
86
87 for index_tables, row_tables in tables.iterrows():
88 if row_tables['表名'] == table:
89 time_lst = row_tables['可操作时间段(时:分:秒)'].split(',')
90
91 for index_users, row_users in users.iterrows():
92 if row_users['账号'] == user_id:
93 group_id = row_users['所属权限组编号'] # 查找组编号
94 uid = row_users['编号'] # 查找用户编号
95
96 for index_tables, row_tables in tables.iterrows():
97 if row_tables['表名'] == table:
98 table_id = row_tables['编号'] # 查找表编号
99
100 for time_range in time_lst:
101 filt.append(check_time(time_range, time_point))
102
103 if 1 not in filt:
104 return [1, uid, group_id, table_id]
105 else:
106 return [0]
107
108
109actionlog = pd.read_csv("actionlog.csv")
110permissions = pd.read_csv("permissions.csv")
111tables = pd.read_csv("tables.csv")
112users = pd.read_csv("users.csv")
113
114raw_result = []
115
116for index_actionlog, row_actionlog in actionlog.iterrows():
117
118 user_id = row_actionlog['账号']
119 une = user_not_exist(user_id) # 判断账号是否存在
120 if une:
121 raw_result.append([0, 0, 0, row_actionlog['编号']])
122 continue
123
124 table_action = row_actionlog['执行操作']
125 wt = wrong_table(table_action, user_id) # 判断表是否可操作
126 if wt[0]:
127 wt.remove(wt[0])
128 wt.append(row_actionlog['编号'])
129 raw_result.append(wt)
130
131 wo = wrong_operation(table_action, user_id)
132 if wo[0]:
133 wo.remove(wo[0])
134 wo.append(row_actionlog['编号'])
135 raw_result.append(wo)
136
137 action_time = row_actionlog['操作时间']
138 wtime = wrong_time(action_time, user_id)
139 if wtime[0]:
140 wtime.remove(wtime[0])
141 wtime.append(row_actionlog['编号'])
142 raw_result.append(wtime)
143
144 percent = row_actionlog['编号'] / 100
145 print('\r', end='')
146 print(f'进度:{percent}%', end='')
147
148final_str = ''
149final_result = sorted(raw_result, key=lambda lst: (lst[0], lst[1], lst[2], lst[3]))
150for res in final_result:
151 final_str = final_str + '_'.join(list(map(str, res))) + ','
152final_str = final_str[:-1]
153print('\n' + final_str)
154print(hash_calc(final_str))
DASCTF{271b1ffebf7a76080c7a6e134ae4c929}
出了 WP,确实是对了。哈哈