shellcode加载的几种方式

前言

什么是shellcode

什么是shellcode加载器

shellcode的加载器的原理

加载方式

函数指针0x01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<Windows.h>
int main(){
//1.申请可读可写可执行的内存
LPVOID lpBuffer = VirtualAlloc(NULL,521,MEM_COMMIT,PAGE_EXCUTE_READWRITE);
//2.写入内存
memcpy(lpBuffer,shellcode,521);
//3.声明函数指针
typedef void(*Shell)();
//4.给函数指针赋值
Shell start = (Shell)lpBuffer;
//5.执行函数
start();
return 0;
}
阅读全文

[ 文章无标题 ]

汇编

调试

1
2
3
4
5
6
7
debug test.exe
-r 查看当前的状态,包括寄存器、 地址和汇编指令
-p 单步步过
-t 单步步入
-u 查看汇编代码,在输入u接着显示
-d 0d9a 显示内存
-g 退出

指令

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
call
ret
;加减法:
add p1,p2 ;两数相加把结果放p1
inc ax ;自增相当于ax++
sub p1,p2;两数相减把结果放p1
dec ax ;自减相当于ax--
loop ;每循环一次CX(计数寄存器)的值减一

cmp p1,p2;两数相减比较
CF:进位位,有进位或借位为1
ZF:零值位,计算结果为0则为1
cmp ax,bx;
ax- bx ==0 -> CF:0 ZF:1
ax!=bx-> ax-bx!=0 -> ZF:0
ax<bx -> ax-bx<0 -> CF:1 ZF:0
ab>bx -> ax-bx>0 -> CF:0 ZF:0
ax<=bx -> ax-bx<=0 -> CF:1 || ZF:1
ax>=bx -> ax -bx>=0 -> CF:0 || ZF:1


jmp addr;无条件跳转,不依赖条件
je/jz (标号/地址);条件转移,等于则跳转,ZF=1
jne addr;不等于则跳转,ZF=0则跳转
jb addr; 低于则跳转 CF=1
ja addr; 高于则跳转 CF=0 && ZF=0
jnb addr;不低于则跳转
jna addr;不高于则跳转

“函数”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
assume cs:code
code segment
;定义一个函数
btadd PROC
;取ax,bx相加并把结果保存在ax
add ax,bx
;返回
ret
btadd ENDP
;程序入口
start:
mov ax,3
mov bx,4
call btadd
;退出
mov ax,4C00H
;DOS中断
int 21
code ends
end start

Flag寄存器

image-20240113160208268

windows系统编程

进程相关基础

创建进程

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
int CreateProcessDemo()
{
/*
DWORD cb; 当前结构的大小,需要事先填充,防止后续新增字段导致结构体大小变更
LPWSTR lpReserved; 保留字段未使用
LPWSTR lpDesktop; 指定桌面名称
LPWSTR lpTitle; 控制台程序的窗口标题
DWORD dwX; 新窗口的位置信息和尺寸信息
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars; 新窗口控制台可以显示的行数和列数
DWORD dwYCountChars;
DWORD dwFillAttribute;指定控制台程序的背景颜色
DWORD dwFlags; 标志,指定当前结构的哪些成员是有效的
WORD wShowWindow; 窗口的显示方式是什么
WORD cbReserved2; 保留字段
LPBYTE lpReserved2; 保留字段
HANDLE hStdInput; 标准输入句柄
HANDLE hStdOutput; 标准输出句柄
HANDLE hStdError; 标准错误句柄
*/
STARTUPINFO StartupInfo{};
StartupInfo.cb = sizeof(StartupInfo);
PROCESS_INFORMATION ProcessInformation;
BOOL rtn = CreateProcess(
//_In_opt_ LPCWSTR lpApplicationName,
L"G:\\Tools\\Layer5.0SAINTSEC\\Layer.exe",//想要执行的程序路径
//_Inout_opt_ LPWSTR lpCommandLine,
NULL,//要传递给可执行模块的参数
//_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
NULL,//进程安全属性,如果是NULLL则使用默认的安全属性
//_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
NULL,//线程安全属性,如果是NULLL则使用默认的安全属性
//_In_ BOOL bInheritHandles,
FALSE,//指定了当前进程中的可继承句柄是否可以被启动的新进程所继承
//_In_ DWORD dwCreationFlags,
0,//指定新进程的优先级和创建标志
//_In_opt_ LPVOID lpEnvironment,
NULL,//指定新进程使用的环境变量
//_In_opt_ LPCWSTR lpCurrentDirectory,
NULL,//指定新进程使用的当前目录
//_In_ LPSTARTUPINFOW lpStartupInfo,
&StartupInfo,//启动信息
//_Out_ LPPROCESS_INFORMATION lpProcessInformation
&ProcessInformation //进程相关信息
);
if (!rtn) {
printf("Create Process Field!");
}
else {
printf("dwProcessId:\t%d\n", ProcessInformation.dwProcessId);
printf("dwThreadId:\t%d\n", ProcessInformation.dwThreadId);
printf("hProcess:\t%d\n", ProcessInformation.hProcess);
printf("hThread:\t%d\n", ProcessInformation.hThread);
}
system("pause");
return 0;
}

退出进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//退出当前进程,参数为退出码
ExitProcess(0);
//打开一个进程并退出
void TerminateProcessDemo() {
HANDLE hProcess = OpenProcess(
//_In_ DWORD dwDesiredAccess,
PROCESS_ALL_ACCESS,//权限
//_In_ BOOL bInheritHandle,
FALSE,//返回的句柄是否可以继承
//_In_ DWORD dwProcessId
9940//打开进程的唯一标识符,进程ID
);
if (hProcess==INVALID_HANDLE_VALUE){
printf("Open Process Field!\n");
}else{
TerminateProcess(hProcess,9940);
}
}

遍历进程

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
void traversalProcessDemo() {
/*
#define TH32CS_SNAPHEAPLIST 0x00000001 堆 需要指定第二个参数中的进程ID
#define TH32CS_SNAPPROCESS 0x00000002 进程 不需要指定第二个参数中的进程ID
#define TH32CS_SNAPTHREAD 0x00000004 线程 需要指定第二个参数中的进程ID
#define TH32CS_SNAPMODULE 0x00000008 模块 需要指定第二个参数中的进程ID
*/
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
/*
DWORD dwSize; 本结构的尺寸
DWORD cntUsage; 当前的引用计数
DWORD th32ProcessID; 进程的ID
ULONG_PTR th32DefaultHeapID; 当前进程的默认堆的ID
DWORD th32ModuleID; 模块ID
DWORD cntThreads; 当前进程的线程总数
DWORD th32ParentProcessID; 父进程ID
LONG pcPriClassBase; 当前进程创建的线程的基本优先级
DWORD dwFlags; 标志位
WCHAR szExeFile[MAX_PATH]; 进程对应的文件名
*/
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
bool rtn = Process32First(hSnapshot, &pe32);
while (rtn){
wprintf(L"ProcessName:%s\n", pe32.szExeFile);
printf("ProcessID:%d\n", pe32.th32ProcessID);
printf("------------------------\n");
rtn = Process32Next(hSnapshot, &pe32);
}
}

线程相关基础

创建线程,等待线程

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
DWORD ThreadCallBackA(LPVOID lpThreadParameter) {
for (int i = 0; i < 100000; i++) {
printf("Thread A Output %d\n", i);
}
return 0;
}
DWORD ThreadCallBackB(LPVOID lpThreadParameter) {
for (int i = 0; i < 100000; i++) {
printf("Thread B Output %d\n", i);
}
return 0;
}
int main() {
//-------------以下为线程A------------------
DWORD lpThreadIdA=0;
HANDLE hThreadA = CreateThread(
//_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
NULL,//安全属性
//_In_ SIZE_T dwStackSize,
NULL,//堆栈的初始大小
//_In_ LPTHREAD_START_ROUTINE lpStartAddress,
(LPTHREAD_START_ROUTINE)ThreadCallBackA,//线程启动地址,函数地址,也可以理解为开启一个新的线程去调用该函数
//_In_opt_ __drv_aliasesMem LPVOID lpParameter,
NULL,//线程函数的参数
//_In_ DWORD dwCreationFlags,
NULL,//启动标记,0x0为直接运行,0x4(CREATE_SUSPENDED)为挂起运行
//_Out_opt_ LPDWORD lpThreadId
&lpThreadIdA
);
//等待线程
WaitForSingleObject(hThreadA, INFINITE);
//-------------以下为线程B------------------
DWORD lpThreadIdB = 0;
HANDLE hThreadB = CreateThread(
//_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
NULL,//安全属性
//_In_ SIZE_T dwStackSize,
NULL,//堆栈的初始大小
//_In_ LPTHREAD_START_ROUTINE lpStartAddress,
(LPTHREAD_START_ROUTINE)ThreadCallBackB,//线程启动地址,函数地址,也可以理解为开启一个新的线程去调用该函数
//_In_opt_ __drv_aliasesMem LPVOID lpParameter,
NULL,//线程函数的参数
//_In_ DWORD dwCreationFlags,
NULL,//启动标记,0x0为直接运行,0x4(CREATE_SUSPENDED)为挂起运行
//_Out_opt_ LPDWORD lpThreadId
&lpThreadIdB
);
WaitForSingleObject(hThreadB, INFINITE);
for (int i = 0; i < 100000; i++) {
printf("main Output %d\n", i);
}
return 0;
}

遍历线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void TraversalThreadDemo() {
DWORD dwProcessID = 10204;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);
THREADENTRY32 te32;
te32.dwSize = sizeof(te32);
BOOL rtn = Thread32First(hSnapshot, &te32);
while (rtn) {
if (te32.th32OwnerProcessID == dwProcessID) {
printf("ThreadID: % d\n", te32.th32ThreadID);
printf("OwnerProcessID: % d\n", te32.th32OwnerProcessID);
printf("BasePri: % d\n", te32.tpBasePri);
printf("---------------------------------------\n");
}
rtn = Thread32Next(hSnapshot, &te32);
}
}

结束线程,挂起线程,恢复线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void TraversalThreadDemo() {
DWORD dwProcessID = 24848;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);
THREADENTRY32 te32;
te32.dwSize = sizeof(te32);
BOOL rtn = Thread32First(hSnapshot, &te32);
while (rtn) {
if (te32.th32OwnerProcessID == dwProcessID) {
printf("ThreadID: % d\n", te32.th32ThreadID);
printf("OwnerProcessID: % d\n", te32.th32OwnerProcessID);
printf("BasePri: % d\n", te32.tpBasePri);
printf("---------------------------------------\n");
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
//结束线程
TerminateThread(hThread, -1);
//挂起线程
//SuspendThread(hThread);
//恢复线程
//ResumeThread(hThread);
}
rtn = Thread32Next(hSnapshot, &te32);
}
}

完整代码:

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
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <stdio.h>
#include <Windows.h>
#include <tlhelp32.h>
int CreateProcessDemo()
{
/*
DWORD cb; 当前结构的大小,需要事先填充,防止后续新增字段导致结构体大小变更
LPWSTR lpReserved; 保留字段未使用
LPWSTR lpDesktop; 指定桌面名称
LPWSTR lpTitle; 控制台程序的窗口标题
DWORD dwX; 新窗口的位置信息和尺寸信息
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars; 新窗口控制台可以显示的行数和列数
DWORD dwYCountChars;
DWORD dwFillAttribute;指定控制台程序的背景颜色
DWORD dwFlags; 标志,指定当前结构的哪些成员是有效的
WORD wShowWindow; 窗口的显示方式是什么
WORD cbReserved2; 保留字段
LPBYTE lpReserved2; 保留字段
HANDLE hStdInput; 标准输入句柄
HANDLE hStdOutput; 标准输出句柄
HANDLE hStdError; 标准错误句柄
*/
STARTUPINFO StartupInfo{};
StartupInfo.cb = sizeof(StartupInfo);
PROCESS_INFORMATION ProcessInformation;
BOOL rtn = CreateProcess(
//_In_opt_ LPCWSTR lpApplicationName,
L"G:\\Tools\\Layer5.0SAINTSEC\\Layer.exe",//想要执行的程序路径
//_Inout_opt_ LPWSTR lpCommandLine,
NULL,//要传递给可执行模块的参数
//_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
NULL,//进程安全属性,如果是NULLL则使用默认的安全属性
//_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
NULL,//线程安全属性,如果是NULLL则使用默认的安全属性
//_In_ BOOL bInheritHandles,
FALSE,//指定了当前进程中的可继承句柄是否可以被启动的新进程所继承
//_In_ DWORD dwCreationFlags,
0,//指定新进程的优先级和创建标志
//_In_opt_ LPVOID lpEnvironment,
NULL,//指定新进程使用的环境变量
//_In_opt_ LPCWSTR lpCurrentDirectory,
NULL,//指定新进程使用的当前目录
//_In_ LPSTARTUPINFOW lpStartupInfo,
&StartupInfo,//启动信息
//_Out_ LPPROCESS_INFORMATION lpProcessInformation
&ProcessInformation //进程相关信息
);
if (!rtn) {
printf("Create Process Field!");
}
else {
printf("dwProcessId:\t%d\n", ProcessInformation.dwProcessId);
printf("dwThreadId:\t%d\n", ProcessInformation.dwThreadId);
printf("hProcess:\t%d\n", ProcessInformation.hProcess);
printf("hThread:\t%d\n", ProcessInformation.hThread);
}
system("pause");
return 0;
}
void TerminateProcessDemo() {
HANDLE hProcess = OpenProcess(
//_In_ DWORD dwDesiredAccess,
PROCESS_ALL_ACCESS,//权限
//_In_ BOOL bInheritHandle,
FALSE,//返回的句柄是否可以继承
//_In_ DWORD dwProcessId
9940//打开进程的唯一标识符,进程ID
);
if (hProcess==INVALID_HANDLE_VALUE){
printf("Open Process Field!\n");
}else{
TerminateProcess(hProcess,9940);
}
}
void TraversalProcessDemo() {
/*
#define TH32CS_SNAPHEAPLIST 0x00000001 堆 需要指定第二个参数中的进程ID
#define TH32CS_SNAPPROCESS 0x00000002 进程 不需要指定第二个参数中的进程ID
#define TH32CS_SNAPTHREAD 0x00000004 线程 需要指定第二个参数中的进程ID
#define TH32CS_SNAPMODULE 0x00000008 模块 需要指定第二个参数中的进程ID
*/
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
/*
DWORD dwSize; 本结构的尺寸
DWORD cntUsage; 当前的引用计数
DWORD th32ProcessID; 进程的ID
ULONG_PTR th32DefaultHeapID; 当前进程的默认堆的ID
DWORD th32ModuleID; 模块ID
DWORD cntThreads; 当前进程的线程总数
DWORD th32ParentProcessID; 父进程ID
LONG pcPriClassBase; 当前进程创建的线程的基本优先级
DWORD dwFlags; 标志位
WCHAR szExeFile[MAX_PATH]; 进程对应的文件名
*/
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
bool rtn = Process32First(hSnapshot, &pe32);
while (rtn){
wprintf(L"ProcessName:%s\n", pe32.szExeFile);
printf("ProcessID:%d\n", pe32.th32ProcessID);
printf("------------------------\n");
rtn = Process32Next(hSnapshot, &pe32);
}
}


DWORD ThreadCallBackA(LPVOID lpThreadParameter) {
for (int i = 0; i < 100000; i++) {
printf("Thread A Output %d\n", i);
}
return 0;
}
DWORD ThreadCallBackB(LPVOID lpThreadParameter) {
for (int i = 0; i < 100000; i++) {
printf("Thread B Output %d\n", i);
}
return 0;
}
int CreateThreadDemo() {
//-------------以下为线程A------------------
DWORD lpThreadIdA=0;
HANDLE hThreadA = CreateThread(
//_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
NULL,//安全属性
//_In_ SIZE_T dwStackSize,
NULL,//堆栈的初始大小
//_In_ LPTHREAD_START_ROUTINE lpStartAddress,
(LPTHREAD_START_ROUTINE)ThreadCallBackA,//线程启动地址,函数地址,也可以理解为开启一个新的线程去调用该函数
//_In_opt_ __drv_aliasesMem LPVOID lpParameter,
NULL,//线程函数的参数
//_In_ DWORD dwCreationFlags,
NULL,//启动标记,0x0为直接运行,0x4(CREATE_SUSPENDED)为挂起运行
//_Out_opt_ LPDWORD lpThreadId
&lpThreadIdA
);
//等待线程
WaitForSingleObject(hThreadA, INFINITE);
//-------------以下为线程B------------------
DWORD lpThreadIdB = 0;
HANDLE hThreadB = CreateThread(
//_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
NULL,//安全属性
//_In_ SIZE_T dwStackSize,
NULL,//堆栈的初始大小
//_In_ LPTHREAD_START_ROUTINE lpStartAddress,
(LPTHREAD_START_ROUTINE)ThreadCallBackB,//线程启动地址,函数地址,也可以理解为开启一个新的线程去调用该函数
//_In_opt_ __drv_aliasesMem LPVOID lpParameter,
NULL,//线程函数的参数
//_In_ DWORD dwCreationFlags,
NULL,//启动标记,0x0为直接运行,0x4(CREATE_SUSPENDED)为挂起运行
//_Out_opt_ LPDWORD lpThreadId
&lpThreadIdB
);
WaitForSingleObject(hThreadB, INFINITE);
for (int i = 0; i < 100000; i++) {
printf("main Output %d\n", i);
}
return 0;
}

void TraversalThreadDemo() {
DWORD dwProcessID = 24848;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID);
THREADENTRY32 te32;
te32.dwSize = sizeof(te32);
BOOL rtn = Thread32First(hSnapshot, &te32);
while (rtn) {
if (te32.th32OwnerProcessID == dwProcessID) {
printf("ThreadID: % d\n", te32.th32ThreadID);
printf("OwnerProcessID: % d\n", te32.th32OwnerProcessID);
printf("BasePri: % d\n", te32.tpBasePri);
printf("---------------------------------------\n");
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
//结束线程
TerminateThread(hThread, -1);
//挂起线程
//SuspendThread(hThread);
//ResumeThread(hThread);
}
rtn = Thread32Next(hSnapshot, &te32);
}
}
int main() {
TraversalThreadDemo();
}

TCP协议详解

TCP简介

TCP,即Transmission Control Protocol,传输控制协议。人如其名,要对数据的传输进行一个详细的控制。

image-20230919164323119

阅读全文

DNS协议详解

什么是DNS协议

​ DNS是一种可以将域名和IP地址相互映射的以层次结构分布的数据库系统。DNS系统采用递归查询请求的方式来响应用户的查询,为互联网的运行提供关键性的基础服务。目前绝大多数的防火墙和网络都会开放DNS服务,DNS数据包不会被拦截,因此可以基于DNS协议建立隐蔽信道,从而顺利穿过防火墙,在客户端和服务器之间传输数据。

DNS服务器

​ 域名对应的IP地址都保存在DNS服务器。我们输入域名,浏览器就会在后台自动向DNS服务器发出请求,获取对应的IP地址。这就是DNS查询。

image-20230912163342906

​ 举个栗子,当我在浏览器输入www.ixxzhi.cn这个域名,浏览器就要像DNS服务器查询www.ixxzhi.cn对应的IP地址是什么,然后向该IP发出访问请求。

​ 网上有很多公用的DNS服务器,我们选择Cloudflare公司提供的1.1.1.1进行演示。

阅读全文

kerberos(待更新)

Kerberos协议

Kerberos概述

​ Kerberos是一种由MIT(麻省理工大学)提出的一种网络身份验证协议。它旨在通过使用密钥加密技术为客户端/服务器应用程序提供强身份验证。

img

Kerberos基本概念

  • Key Distribution Center,or KDC

    在启用Kerberos的环境中进行身份验证的受信任源。

  • Kerberos KDC Server

    作为密钥分发中心(KDC)的计算机或服务器。

  • Kerberos Client

    集群中针对KDC进行身份验证的任何计算机。

  • KDC Admin Account

    Ambari

在Kerberos协议中,主要有以下三个角色:

  • 访问服务的客户端:kerberos客户端是代表需要访问资源的用户进行操作的应用程序,例如打开文件、查询数据库或打印文档。每个Kerberos客户端在访问资源之前都会请求身份验证。
  • 提供服务的服务端:域内提供服务的服务端,服务端都有一个独唯一的SPN。
  • 提供认证服务的KDC:Key Distribution Center,密钥分发中心是一种网络服务,它向活动目录域内的用户和计算机提供会话票据和临时会话密钥,其服务账户为krbtgt。KDC作为活动目录域服务AADS的一部分运行在每个域控制器上。
  • 这里说一下krbtgt账户,该用户是在创建活动目录时系统自动创建的一个账号,其作用是KDC密钥分发中心的服务账号,其密码是系统随机生成的,无法正常登陆主机。

Kerberos是一种基于票据Ticket的认证方式。客户端想要访问服务端的某个服务,首先需要购买服务端认可的ST服务票据(Server Ticket)。也就是说,客户端在访问服务之前需要先买好票,等待服务验票之后才能访问。但是这张票并不能直接购买,需要一张TGT认购权证(Ticket Granting Ticket)。也就是说,客户端在买票之前必须先获得一张TGT认购权证。TGT认购权证ST服务票据均是由KDC发放;因为KDC是运行在域控制器上,所以说TGT认购权证和ST服务票据均是由域控分发。

Kerberos使用TCP/UDP 88端口进行认证,使用TCP/UDP 464端口进行密码重置。

Kerberos中一些名词的简称及含义如表所示:

简称 英文全程 中文名称
DC Domain Controller 域控制器
krbtgt KDC密钥分发中心服务账户
KDC Key Distribution Center 密钥分发中心,由域控担任
AD Active Directory 活动目录,里面包含域内用户数据库
AS Authentication Service 认证服务
TGS Ticket Granting Service 票据授予服务
TGT Ticket Granting Ticket TGT认购权证,由KDC的AS认证服务发放
ST Service Ticket ST服务票据,由KDC的TGS票据授予服务发放

​ Kerberos协议有两个基础认证模块:AS_REQ & AS_REPTGS_REQ & TGS_REP,以及微软扩展的两个认证模块S4UPAC。S4U是微软为了实现委派而扩展的模块,分为S4U2Self和S4U2Proxy。在Kerberos最初设计的流程里只有说明了如何证明客户端的真实身份,但是并没有说明客户端是否有权限访问该服务,因为在域中不同权限的用户能够访问的资源是不同的。因此微软为了解决权限这个问题,引入了PAC(Privilege Attribute Certificate,特权属性证书)的概念。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sequenceDiagram
participant c as 客户端
participant s as 服务器
participant sms as SMS服务器
participant yys as 三大运营商
participant jz as 基站
Title: 基础网络身份验证,Cookie,JWT
c->>s: SMS Request
note over s:生成验证码并保存到服务器
s->>sms: SMS Request
sms-->>yys: SMS Request
yys-->>jz: SMS Code
jz-->>用户手机: SMS Code
note over 用户手机:用户接收验证码
sms->>s:SMS Response
s->>c:SMS Response
c->>s:Login Requst
s->>c:Response

认证过程:

  1. AS_REQ:Client向KDC发起AS_REQ,请求凭据是Client Hash加密的时间戳
  2. AS_RESP:KDC使用Client Hash进行解密,如果结果正确就返回用krbtgt hash加密的TGT票据,TGT里面包含PAC,PAC包含Client的sid、Client所在的组
  3. TGS_REQ:Client凭借TGT票据向KDC发起针对特定服务的TGS_REQ请求
  4. TGS_RESP:KDC使用krbtgt hash进行解密,如果结果正确,就返回用服务hash加密的ST票据(这一步不管用户有没有访问服务的权限,只要TGT正确就返回ST票据)
  5. AP_REQ:Client拿着ST票据去请求服务
  6. AP_RESP:服务使用自己的hash解密ST票据。如果解密正确,就拿着PAC去KDC那边问Client有没有访问权限,域控解密PAC。获取Client的sid,以及所在的组,再根据该服务的ACL,判断Client是否有访问服务的权限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
sequenceDiagram
Title: Kerberos
participant C as Client
participant AS as Authenticaton Service
participant TGS as Ticket Granting Service
participant SS as Server Service
C->>AS: AS_REQ
Note left of C:ClientHash加密的时间戳
AS->>C:AS_RESP
alt 账密正确
AS-->>C:krbtgt Hash加密的TGT
end
C->>TGS: TGS_REQ
note left of C: Krbtgt hashi加密的TGT
TGS->>C: TGS_RESP
alt 解密正确
TGS-->>C:用服务Hash加密的ST
end
C->>SS: AP_REQ
note left of C:用服务Hash加密的ST
SS->>C: AP_RESP

image-20231209020706062

yak学习笔记

Yaklang语言简介

Yaklang 是一门上下文无关文法定义的 图灵完备 的程序语言,他基于 YakVM 运行,语法规则定义了 Yaklang 的语言结构

Yaklang 是一个 动态强类型语言

  1. Yaklang 允许用户可以在改变变量的值的时候也改变变量的类型;
  2. 在进行表达式运算的时候 Yaklang 允许程序或函数识别运行时的准确类型,并进行对应计算;

Yaklang 可以 编译 为 YakVM 可以支持的字节码运行;

Yaklang 语法博采众长,非常容易上手与学习;

Yaklang 可以作为一门“嵌入式语言”被其他语言调用或编译;

Yaklang 是业内第一门 CDSL (Cybersecurity Domain Specific Language),使用图灵完备特性融合原子化的安全能力。

阅读全文