You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

compromised_hosts.lua 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. local reconf = config['regexp']
  2. local rspamd_regexp = require 'rspamd_regexp'
  3. local util = require 'rspamd_util'
  4. reconf['HAS_PHPMAILER_SIG'] = {
  5. -- PHPMailer 6.0.0 and older used hex hash in boundary:
  6. -- boundary="b1_2a45d5e29f78d3408e318878b049f474"
  7. -- Since 6.0.1 it uses base64 (without =+/):
  8. -- boundary="b1_uBN0UPD3n6RU04VPxI54tENiDgaCGoh15l9s73oFnlM"
  9. -- boundary="b1_Ez5tmpb4bSqknyUZ1B1hIvLAfR1MlspDEKGioCOXc"
  10. -- https://github.com/PHPMailer/PHPMailer/blob/v6.4.0/src/PHPMailer.php#L2660
  11. re = [[X-Mailer=/^PHPMailer /H || Content-Type=/boundary="b1_[0-9a-zA-Z]+"/H]],
  12. description = "PHPMailer signature",
  13. group = "compromised_hosts"
  14. }
  15. reconf['PHP_SCRIPT_ROOT'] = {
  16. re = "X-PHP-Originating-Script=/^0:/Hi",
  17. description = "PHP Script executed by root UID",
  18. score = 1.0,
  19. group = "compromised_hosts"
  20. }
  21. reconf['HAS_X_POS'] = {
  22. re = "header_exists('X-PHP-Originating-Script')",
  23. description = "Has X-PHP-Originating-Script header",
  24. group = "compromised_hosts"
  25. }
  26. reconf['HAS_X_PHP_SCRIPT'] = {
  27. re = "header_exists('X-PHP-Script')",
  28. description = "Has X-PHP-Script header",
  29. group = "compromised_hosts"
  30. }
  31. -- X-Source:
  32. -- X-Source-Args: /usr/sbin/proxyexec -q -d -s /var/run/proxyexec/cagefs.sock/socket /bin/cagefs.server
  33. -- X-Source-Dir: silvianimberg.com:/public_html/wp-content/themes/ultimatum
  34. reconf['HAS_X_SOURCE'] = {
  35. re = "header_exists('X-Source') || header_exists('X-Source-Args') || header_exists('X-Source-Dir')",
  36. description = "Has X-Source headers",
  37. group = "compromised_hosts"
  38. }
  39. -- X-Authenticated-Sender: accord.host-care.com: sales@cortaflex.si
  40. rspamd_config.HAS_X_AS = {
  41. callback = function (task)
  42. local xas = task:get_header('X-Authenticated-Sender')
  43. if not xas then return false end
  44. local _,_,auth = xas:find('[^:]+:%s(.+)$')
  45. if auth then
  46. -- TODO: see if we can parse an e-mail address from auth
  47. -- and see if it matches the from address or not
  48. return true, auth
  49. else
  50. return true
  51. end
  52. end,
  53. description = 'Has X-Authenticated-Sender header',
  54. group = "compromised_hosts",
  55. score = 0.0
  56. }
  57. -- X-Get-Message-Sender-Via: accord.host-care.com: authenticated_id: sales@cortaflex.si
  58. rspamd_config.HAS_X_GMSV = {
  59. callback = function (task)
  60. local xgmsv = task:get_header('X-Get-Message-Sender-Via')
  61. if not xgmsv then return false end
  62. local _,_,auth = xgmsv:find('authenticated_id: (.+)$')
  63. if auth then
  64. -- TODO: see if we can parse an e-mail address from auth
  65. -- and see if it matches the from address or not.
  66. return true, auth
  67. else
  68. return true
  69. end
  70. end,
  71. description = 'Has X-Get-Message-Sender-Via: header',
  72. group = "compromised_hosts",
  73. score = 0.0,
  74. }
  75. -- X-AntiAbuse: This header was added to track abuse, please include it with any abuse report
  76. -- X-AntiAbuse: Primary Hostname - accord.host-care.com
  77. -- X-AntiAbuse: Original Domain - swaney.com
  78. -- X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12]
  79. -- X-AntiAbuse: Sender Address Domain - dropbox.com
  80. reconf['HAS_X_ANTIABUSE'] = {
  81. re = "header_exists('X-AntiAbuse')",
  82. description = "Has X-AntiAbuse headers",
  83. group = "compromised_hosts"
  84. }
  85. reconf['X_PHP_EVAL'] = {
  86. re = [[X-PHP-Script=/eval\(\)'d code/H || X-PHP-Originating-Script=/eval\(\)'d code/H]],
  87. description = "Message sent using eval'd PHP",
  88. score = 4.0,
  89. group = "compromised_hosts"
  90. }
  91. reconf['HAS_WP_URI'] = {
  92. re = '/\\/wp-[^\\/]+\\//Ui',
  93. description = "Contains WordPress URIs",
  94. one_shot = true,
  95. group = "compromised_hosts"
  96. }
  97. reconf['WP_COMPROMISED'] = {
  98. re = '/\\/wp-(?:content|includes)[^\\/]+\\//Ui',
  99. description = "URL that is pointing to a compromised WordPress installation",
  100. one_shot = true,
  101. group = "compromised_hosts"
  102. }
  103. reconf['PHP_XPS_PATTERN'] = {
  104. re = 'X-PHP-Script=/^[^\\. ]+\\.[^\\.\\/ ]+\\/sendmail\\.php\\b/Hi',
  105. description = "Message contains X-PHP-Script pattern",
  106. group = "compromised_hosts"
  107. }
  108. reconf['HAS_XAW'] = {
  109. re = "header_exists('X-Authentication-Warning')",
  110. description = "Has X-Authentication-Warning header",
  111. group = "compromised_hosts"
  112. }
  113. -- X-Authentication-Warning: localhost.localdomain: www-data set sender to info@globalstock.lv using -f
  114. reconf['XAW_SERVICE_ACCT'] = {
  115. re = "X-Authentication-Warning=/\\b(?:www-data|anonymous|ftp|apache|nobody|guest|nginx|web|www) set sender to\\b/Hi",
  116. description = "Message originally from a service account",
  117. score = 1.0,
  118. group = "compromised_hosts"
  119. }
  120. reconf['ENVFROM_SERVICE_ACCT'] = {
  121. re = "check_smtp_data('from',/^(?:www-data|anonymous|ftp|apache|nobody|guest|nginx|web|www)@/i)",
  122. description = "Envelope from is a service account",
  123. score = 1.0,
  124. group = "compromised_hosts"
  125. }
  126. reconf['HIDDEN_SOURCE_OBJ'] = {
  127. re = "X-PHP-Script=/\\/\\..+/Hi || X-PHP-Originating-Script=/(?:^\\d+:|\\/)\\..+/Hi || X-Source-Args=/\\/\\..+/Hi",
  128. description = "UNIX hidden file/directory in path",
  129. score = 2.0,
  130. group = "compromised_hosts"
  131. }
  132. local hidden_uri_re = rspamd_regexp.create_cached('/(?!\\/\\.well[-_]known\\/)(?:^\\.[A-Za-z0-9]|\\/'..
  133. '\\.[A-Za-z0-9]|\\/\\.\\.\\/)/i')
  134. rspamd_config.URI_HIDDEN_PATH = {
  135. callback = function (task)
  136. local urls = task:get_urls(false)
  137. if (urls) then
  138. for _, url in ipairs(urls) do
  139. if (not (url:is_subject() and url:is_html_displayed())) then
  140. local path = url:get_path()
  141. if (hidden_uri_re:match(path)) then
  142. -- TODO: need url:is_schemeless() to improve this
  143. return true, 1.0, url:get_text()
  144. end
  145. end
  146. end
  147. end
  148. end,
  149. description = 'Message contains URI with a hidden path',
  150. score = 1.0,
  151. group = 'compromised_hosts',
  152. }
  153. reconf['MID_RHS_WWW'] = {
  154. re = "Message-Id=/@www\\./Hi",
  155. description = "Message-ID from www host",
  156. score = 0.5,
  157. group = "compromised_hosts"
  158. }
  159. rspamd_config.FROM_SERVICE_ACCT = {
  160. callback = function (task)
  161. local re = rspamd_regexp.create_cached('/^(?:www-data|anonymous|ftp|apache|nobody|guest|nginx|web|www)@/i');
  162. -- From
  163. local from = task:get_from(2)
  164. if (from and from[1]) then
  165. if (re:match(from[1].addr)) then return true end
  166. end
  167. -- Sender
  168. local sender = task:get_header('Sender')
  169. if sender then
  170. local s = util.parse_mail_address(sender, task:get_mempool())
  171. if (s and s[1]) then
  172. if (re:match(s[1].addr)) then return true end
  173. end
  174. end
  175. -- Reply-To
  176. local replyto = task:get_header('Reply-To')
  177. if replyto then
  178. local rt = util.parse_mail_address(replyto, task:get_mempool())
  179. if (rt and rt[1]) then
  180. if (re:match(rt[1].addr)) then return true end
  181. end
  182. end
  183. end,
  184. description = "Sender/From/Reply-To is a service account",
  185. score = 1.0,
  186. group = "compromised_hosts"
  187. }
  188. reconf['WWW_DOT_DOMAIN'] = {
  189. re = "From=/@www\\./Hi || Sender=/@www\\./Hi || Reply-To=/@www\\./Hi || check_smtp_data('from',/@www\\./i)",
  190. description = "From/Sender/Reply-To or Envelope is @www.domain.com",
  191. score = 0.5,
  192. group = "compromised_hosts"
  193. }