Skip to content

Instantly share code, notes, and snippets.

@lkraider
Created June 14, 2020 21:25
Show Gist options
  • Save lkraider/9530798a695586fc1580d0728966f6f0 to your computer and use it in GitHub Desktop.
Save lkraider/9530798a695586fc1580d0728966f6f0 to your computer and use it in GitHub Desktop.
X-OVH-SPAMCAUSE decoder
def decode(msg):
text = []
for i in range(0, len(msg), 2):
text.append(unrot(msg[i: i + 2]))
return str.join('', text)
def unrot(pair, key=ord('x')):
offset = 0
for c in 'cdefgh':
if c in pair:
offset = (ord('g') - ord(c)) * 16
break
return chr(sum(ord(c) for c in pair) - key - offset)
if __name__ == '__main__':
import sys
print(decode(sys.argv[1]))
@DoubleYouEl
Copy link

The offset characters (c..g) are alternatingly appended and prepended. So instead of just checking if one of them is in the pair, it is necessary to differentiate between, e.g., fh and hf, by keeping track of even or odd pairs. Check my fork for an update.

@plegrand1
Copy link

Hello,
can you explain me how to use it ?
where i have to put the spamcause string ?
Thanks for your help

@DoubleYouEl
Copy link

DoubleYouEl commented Aug 26, 2021

The spamcause string is the parameter of the decode function, given as command line parameter in the main.
So in most Python environments, you could use the following:
python spam_decode.py "spamcause string"
(but without the quotes)

@plegrand1
Copy link

Hello and thanks for your answer

you mean
python spam_decode.py my_very_long_string ?

@plegrand1
Copy link

yes thanks a lot it works fine !

@plegrand1
Copy link

Just to know, is it normal that the result is truncated
Thanks again

@DoubleYouEl
Copy link

Can you provide an example? In principle, the entire spamcause string is parsed and decoded.

@arbiyassine17
Copy link

is there any updte for this vad decoder? it cannot decrypt this
X-Spamcause: dmFkZTEXAf7aJW+CObm3ConPoxO2B57jkK1eWV41nKjpNyF5O9Fedv3pf9sOxvbr3kehXrA2ml2Wx4tdJFDANYeUI5UPAa+44GGqT5Wk/CegS9N1LE74n0mMi0pnk636eEOiXt7iakkXGOEDL7R6udwHSUkAhrLEWFyU1h87u20MpBmDccfMEjsbWTzuXngth8oBItZl1gW63vQ/l9cLp8ZsSWwnwF4PxdZmUNBIv0dSar9EXKjr6UqJ3Bo0cUCoY+1n1NC1fpwJn3OwEJr3n/jnYTdPTMQXH5QNZ7oI1DXtk9tMtAgNHDxMWa6rZP6kTKBIujzEHieM7p+SBn4x8h31yAPOQBDT7JZZWwk9MKMDw8EoWb+AEE+aVPg03yrV2Ml4Nb1rqFd1FnIHV9ushfYIAdig9tnS7WoK/trJLmlF9UUPzpfKNHpxI/Fco+KJBAZ08YEDfcXxTutCaq9WnMQKNGPACAtP91Mv4lAj9h08OHgrzJbpUSzRWT6MM76wn91NeSo+oJF8gMNkgLS1EkAhQK6tzDFJ+Sdk7CA5z0RyxdPNEJ0O7wEaZ7unxYPMw+cDH+D63nqEJ1mmQTgtj/5GUFZyWoVPxExo4tMETsc8v3jg3GW5V/INZgGUBPoU5VsdFR4peXGmqrb/CRyaMpzqm9IKxgS1Fs88z1utyPjVWILVtg

@DoubleYouEl
Copy link

is there any updte for this vad decoder? it cannot decrypt this X-Spamcause: dmFkZTEXAf7aJW+CObm3ConPoxO2B57jkK1eWV41nKjpNyF5O9Fedv3pf9sOxvbr3kehXrA2ml2Wx4tdJFDANYeUI5UPAa+44GGqT5Wk/CegS9N1LE74n0mMi0pnk636eEOiXt7iakkXGOEDL7R6udwHSUkAhrLEWFyU1h87u20MpBmDccfMEjsbWTzuXngth8oBItZl1gW63vQ/l9cLp8ZsSWwnwF4PxdZmUNBIv0dSar9EXKjr6UqJ3Bo0cUCoY+1n1NC1fpwJn3OwEJr3n/jnYTdPTMQXH5QNZ7oI1DXtk9tMtAgNHDxMWa6rZP6kTKBIujzEHieM7p+SBn4x8h31yAPOQBDT7JZZWwk9MKMDw8EoWb+AEE+aVPg03yrV2Ml4Nb1rqFd1FnIHV9ushfYIAdig9tnS7WoK/trJLmlF9UUPzpfKNHpxI/Fco+KJBAZ08YEDfcXxTutCaq9WnMQKNGPACAtP91Mv4lAj9h08OHgrzJbpUSzRWT6MM76wn91NeSo+oJF8gMNkgLS1EkAhQK6tzDFJ+Sdk7CA5z0RyxdPNEJ0O7wEaZ7unxYPMw+cDH+D63nqEJ1mmQTgtj/5GUFZyWoVPxExo4tMETsc8v3jg3GW5V/INZgGUBPoU5VsdFR4peXGmqrb/CRyaMpzqm9IKxgS1Fs88z1utyPjVWILVtg

This cannot be a correct X-Spamcause string, I'm afraid...

@arbiyassine17
Copy link

no bro it is correct here is the full header

Authentication-Results:mail94c7.megamailservers.com; spf=tempfail smtp.mailfrom=localhost.localdomain
Content-Type:text/html
Date:Wed, 7 May 2025 06:59:20 +0000 (UTC)
Dmarc-Filter:OpenDMARC Filter v1.4.2 mail94c7.megamailservers.com 5476xLl43206121
From:[email protected]
Message-ID:20250507065921.1C1B847E40B@localhost
Mime-Version:1.0
Received:by localhost (Postfix, from userid 0) id 1C1B847E40B; Wed, 7 May 2025 06:59:21 +0000 (UTC)
Return-Path:[email protected]
Subject:SPAM fedex Delivery Waiting
To:[email protected]
X-Envelope-From:[email protected]
X-Origin-Asn:396982
X-Origin-Country:US
X-Rspamd-Result:default: False [6.30 / 6.00]; HFILTER_HELO_5(3.00)[localhost]; DMARC_POLICY_REJECT(2.00)[fedex.com : No valid SPF, No valid DKIM,reject]; MID_RHS_NOT_FQDN(0.50)[]; FORGED_SENDER(0.30)[[email protected],[email protected]]; MIME_HTML_ONLY(0.20)[]; ONCE_RECEIVED(0.20)[]; RCVD_NO_TLS_LAST(0.10)[]; R_DKIM_NA(0.00)[]; MIME_TRACE(0.00)[0:~]; RCVD_COUNT_ONE(0.00)[1]; MISSING_XM_UA(0.00)[]; FROM_NEQ_ENVFROM(0.00)[[email protected],[email protected]]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_NO_DN(0.00)[]; RCPT_COUNT_ONE(0.00)[1]; R_SPF_DNSFAIL(0.00)[temporary DNS error]; ASN(0.00)[asn:396982, ipnet:34.139.192.0/20, country:US]; TO_DN_NONE(0.00)[]; NEURAL_SPAM(0.00)[0.836]; ARC_NA(0.00)[]
X-Rspamd-Status:Yes, score=6.30
X-Spam:Yes
X-Spam-Factor:VADE
X-Spam-Flag:YES
X-Vade-Spamcause:dmFkZTF74xMy5IslKlQPpK1gvXw5/PTzaumShCvGY+Z8PpTElnP2aFtqqcuFX6ZDTCkC36le50IslWryQpehrW79UO5ANoj6u2AsT5lURJfSGXsNsJ0lkIT32Jn8x63CUQBTMQ5GKnSGt34wQLcz9Iw9Kjv581O22AEbmjD9f0a9g6TfxsvCN3NSzhHsTyxuEX9shIaU/lr8QTxpuQ84zb9QPrqD35NGRjrZAcpITiHmsgqpn8tlSLyiv0mRFltixK6H3bN1u61RmRKkaUK8uYjnGjVOapKsoCtt2n5Q/JTo1zlGcrxdlxhEYSXgVCio5FlkmJLbMhgIMa5GK/fZESAcqEv+cS6Lb1JFqQtZyunHPQzzBKetAhJyVpvPoZP4ja0UMopH4RUvebNS5BuIqI8RQkIyU4BkgusrQEkoF2Opo3ddLmVo54AqRkRi0ougPmiKJyDStDoQQroWvDjTAGdHu30reJoUlY0K+j2zQ7TDi6ZaEdvWddYkCAfwdODtzSfTBYnb18SpRRKBF/fROnvZUI3Fbp2BT45j4NFi5FQtWlC1izWr1eywwX/wa3jky8rdqEJVRd/Mes1IZ0PX9VVttk37fPX0nmnVOf69Dmb0GUI3Z1E/5K9ClhlMpsm7jr0jASbRtEKIFw+A00dm14N9+KJg4gusfE/LCzFBRBKYFK/Jdw
X-Vade-Spamscore:500
X-Vade-Spamstate:spam:high
X-Whl:LR

@lkraider
Copy link
Author

lkraider commented May 8, 2025

Looks like a base64 binary content, running base64 decode returns and ASCII prefix: vade1{…} confirming the version marker.

Sample code:

import base64

# The full Base64-encoded X-Vade-Spamcause header value
spamcause_b64 = (
    "dmFkZTF74xMy5IslKlQPpK1gvXw5/PTzaumShCvGY+Z8PpTElnP2aFtqqcuFX6ZDTCkC36le50IslWryQpehrW79UO5ANoj6u2AsT5l"
    "URJfSGXsNsJ0lkIT32Jn8x63CUQBTMQ5GKnSGt34wQLcz9Iw9Kjv581O22AEbmjD9f0a9g6TfxsvCN3NSzhHsTyxuEX9shIaU/lr8Q"
    "TxpuQ84zb9QPrqD35NGRjrZAcpITiHmsgqpn8tlSLyiv0mRFltixK6H3bN1u61RmRKkaUK8uYjnGjVOapKsoCtt2n5Q/JTo1zlGcrx"
    "dlxhEYSXgVCio5FlkmJLbMhgIMa5GK/fZESAcqEv+cS6Lb1JFqQtZyunHPQzzBKetAhJyVpvPoZP4ja0UMopH4RUvebNS5BuIqI8R"
    "QkIyU4BkgusrQEkoF2Opo3ddLmVo54AqRkRi0ougPmiKJyDStDoQQroWvDjTAGdHu30reJoUlY0K+j2zQ7TDi6ZaEdvWddYkCAfwd"
    "ODtzSfTBYnb18SpRRKBF/fROnvZUI3Fbp2BT45j4NFi5FQtWlC1izWr1eywwX/wa3jky8rdqEJVRd/Mes1IZ0PX9VVttk37fPX0n"
    "mnVOf69Dmb0GUI3Z1E/5K9ClhlMpsm7jr0jASbRtEKIFw+A00dm14N9+KJg4gusfE/LCzFBRBKYFK/Jdw"
)

# Ensure correct padding
spamcause_b64 += "=" * ((4 - len(spamcause_b64) % 4) % 4)

# Decode Base64 to raw bytes
decoded = base64.b64decode(spamcause_b64)

# Build mixed ASCII/hex representation
output = []
for b in decoded:
    if 32 <= b <= 126:  # printable ASCII range
        output.append(chr(b))
    else:
        output.append(f"\\x{b:02x}")

mixed_repr = "".join(output)

# Print the result
print(mixed_repr)

STDOUT/STDERR

vade1{\xe3\x132\xe4\x8b%*T\x0f\xa4\xad`\xbd|9\xfc\xf4\xf3j\xe9\x92\x84+\xc6c\xe6|>\x94\xc4\x96s\xf6h[j\xa9\xcb\x85_\xa6CL)\x02\xdf\xa9^\xe7B,\x95j\xf2B\x97\xa1\xadn\xfdP\xee@6\x88\xfa\xbb`,O\x99TD\x97\xd2\x19{\x0d\xb0\x9d%\x90\x84\xf7\xd8\x99\xfc\xc7\xad\xc2Q\x00S1\x0eF*t\x86\xb7~0@\xb73\xf4\x8c=*;\xf9\xf3S\xb6\xd8\x01\x1b\x9a0\xfd\x7fF\xbd\x83\xa4\xdf\xc6\xcb\xc27sR\xce\x11\xecO,n\x11\x7fl\x84\x86\x94\xfeZ\xfcA<i\xb9\x0f8\xcd\xbfP>\xba\x83\xdf\x93FF:\xd9\x01\xcaHN!\xe6\xb2\x0a\xa9\x9f\xcbeH\xbc\xa2\xbfI\x91\x16[b\xc4\xae\x87\xdd\xb3u\xbb\xadQ\x99\x12\xa4iB\xbc\xb9\x88\xe7\x1a5Nj\x92\xac\xa0+m\xda~P\xfc\x94\xe8\xd79Fr\xbc]\x97\x18Da%\xe0T(\xa8\xe4Yd\x98\x92\xdb2\x18\x081\xaeF+\xf7\xd9\x11 \x1c\xa8K\xfeq.\x8boRE\xa9\x0bY\xca\xe9\xc7=\x0c\xf3\x04\xa7\xad\x02\x12rV\x9b\xcf\xa1\x93\xf8\x8d\xad\x142\x8aG\xe1\x15/y\xb3R\xe4\x1b\x88\xa8\x8f\x11BB2S\x80d\x82\xeb+@I(\x17c\xa9\xa3w].eh\xe7\x80*FDb\xd2\x8b\xa0>h\x8a' \xd2\xb4:\x10B\xba\x16\xbc8\xd3\x00gG\xbb}+x\x9a\x14\x95\x8d\x0a\xfa=\xb3C\xb4\xc3\x8b\xa6Z\x11\xdb\xd6u\xd6$\x08\x07\xf0t\xe0\xed\xcd'\xd3\x05\x89\xdb\xd7\xc4\xa9E\x12\x81\x17\xf7\xd1:{\xd9P\x8d\xc5n\x9d\x81O\x8ec\xe0\xd1b\xe4T-ZP\xb5\x8b5\xab\xd5\xec\xb0\xc1\x7f\xf0kx\xe4\xcb\xca\xdd\xa8BUE\xdf\xccz\xcdHgC\xd7\xf5Um\xb6M\xfb|\xf5\xf4\x9ei\xd59\xfe\xbd\x0ef\xf4\x19B7gQ?\xe4\xafB\x96\x19L\xa6\xc9\xbb\x8e\xbd#\x01&\xd1\xb4B\x88\x17\x0f\x80\xd3Gf\xd7\x83}\xf8\xa2`\xe2\x0b\xac|O\xcb\x0b1AD\x12\x98\x14\xaf\xc9w

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment