← All audits

Audit #17 Benign

YARPP – Yet Another Related Posts Plugin · 100k+ installs · baseline 1.0 → head 5.30.11 · suspect committer jeffparker · by austin · closed 2d ago

Actor: Jeff Parker (jeffparker, YARPP)
Show full summary

Suspect-shape but structurally unreachable — benign with one regression to flag. YARPP's version_info() matches the high-confidence catalog IOC unserialize_after_remote_call (@unserialize of wp_remote_post body, hardcoded endpoint, sslverify => false, no signature). The exact same shape that made content-egg's Admitad chain a 7-year latent RCE (audit #15). However, two independent failures make the chain dead code today: (1) the type-gate above the unserialize is always-true, blocking the call, and (2) the live yarpp.org/checkversion.php endpoint returns inert HTML rather than serialized PHP. The author is real and the domain is theirs. No active risk; one defensive regression worth flagging to the author.

Investigated — no compromise found.

Audit retained for the record. No action required.

Plugins under the same committer's SVN access

jeffparker holds push access to 1 plugin totalling 100k+ active installs.

Plugin version history

Every release on wp.org for this plugin, color-coded by relationship to the incident. The compromise window shows where the wp.org Plugin Review Team deleted the malicious tags from SVN — those versions cannot be re-downloaded today.

  1. 1.0 Last clean Last clean release before incident
  2. 🛑 Compromise window 3 days · 2008-01-02 → 2008-01-05

    Malicious releases pushed during this window were deleted from SVN by the wp.org Plugin Review Team. Last malicious tag: 5.30.11.

  3. 1.0.1 PRT cleanup PRT cleanup release — incident closed
  4. 1.1 Clean Clean (post-cleanup)
  5. 1.5 Clean Clean (post-cleanup)
  6. 1.5.1 Clean Clean (post-cleanup)
  7. 2.0.3 Clean Clean (post-cleanup)
  8. 2.0.2 Clean Clean (post-cleanup)
  9. 2.0.1 Clean Clean (post-cleanup)
  10. 2.0 Clean Clean (post-cleanup)
  11. 2.0.4 Clean Clean (post-cleanup)
  12. 2.0.5 Clean Clean (post-cleanup)
  13. 2.0.6 Clean Clean (post-cleanup)
  14. 2.1 Clean Clean (post-cleanup)
  15. 2.1.1 Clean Clean (post-cleanup)
  16. 2.1.2 Clean Clean (post-cleanup)
  17. 2.1.3 Clean Clean (post-cleanup)
  18. 2.1.4 Clean Clean (post-cleanup)
  19. 2.1.5 Clean Clean (post-cleanup)
  20. 3.0b1 Clean Clean (post-cleanup)
  21. 3.0b2 Clean Clean (post-cleanup)
  22. 3.0b3 Clean Clean (post-cleanup)
  23. 3.0b4 Clean Clean (post-cleanup)
  24. 3.0b5 Clean Clean (post-cleanup)
  25. 3.0b6 Clean Clean (post-cleanup)
  26. 3.0b7 Clean Clean (post-cleanup)
  27. 2.1.6 Clean Clean (post-cleanup)
  28. 3.0.1 Clean Clean (post-cleanup)
  29. 3.0.2 Clean Clean (post-cleanup)
  30. 3.0.3 Clean Clean (post-cleanup)
  31. 3.0.4 Clean Clean (post-cleanup)
  32. 3.0.5 Clean Clean (post-cleanup)
  33. 3.0.6 Clean Clean (post-cleanup)
  34. 3.0.7b1 Clean Clean (post-cleanup)
  35. 3.0.7b2 Clean Clean (post-cleanup)
  36. 3.0.7 Clean Clean (post-cleanup)
  37. 3.0.8b1 Clean Clean (post-cleanup)
  38. 3.0.8b2 Clean Clean (post-cleanup)
  39. 3.0.8b3 Clean Clean (post-cleanup)
  40. 3.0.8b4 Clean Clean (post-cleanup)
  41. 3.0.8 Clean Clean (post-cleanup)
  42. 3.0.9 Clean Clean (post-cleanup)
  43. 3.0.10 Clean Clean (post-cleanup)
  44. 3.0.11 Clean Clean (post-cleanup)
  45. 3.0.12 Clean Clean (post-cleanup)
  46. 3.0.13 Clean Clean (post-cleanup)
  47. 3.0.14b1 Clean Clean (post-cleanup)
  48. 3.1b1 Clean Clean (post-cleanup)
  49. 3.1 Clean Clean (post-cleanup)
  50. 3.1.1 Clean Clean (post-cleanup)
  51. 3.1.2b1 Clean Clean (post-cleanup)
  52. 3.1.2 Clean Clean (post-cleanup)
  53. 3.1.3b1 Clean Clean (post-cleanup)
  54. 3.1.3b2 Clean Clean (post-cleanup)
  55. 3.1.3b3 Clean Clean (post-cleanup)
  56. 3.1.3 Clean Clean (post-cleanup)
  57. 3.1.4b2 Clean Clean (post-cleanup)
  58. 3.1.4b3 Clean Clean (post-cleanup)
  59. 3.1.4b4 Clean Clean (post-cleanup)
  60. 3.1.4 Clean Clean (post-cleanup)
  61. 3.1.5 Clean Clean (post-cleanup)
  62. 3.1.6 Clean Clean (post-cleanup)
  63. 3.1.7 Clean Clean (post-cleanup)
  64. 3.1.8 Clean Clean (post-cleanup)
  65. 3.1.9 Clean Clean (post-cleanup)
  66. 3.2b1 Clean Clean (post-cleanup)
  67. 3.2b2 Clean Clean (post-cleanup)
  68. 3.3 Clean Clean (post-cleanup)
  69. 3.3.1 Clean Clean (post-cleanup)
  70. 3.3.2 Clean Clean (post-cleanup)
  71. 3.3.3b1 Clean Clean (post-cleanup)
  72. 3.3.3b2 Clean Clean (post-cleanup)
  73. 3.3.3 Clean Clean (post-cleanup)
  74. 3.4b2 Clean Clean (post-cleanup)
  75. 3.4b3 Clean Clean (post-cleanup)
  76. 3.4b4 Clean Clean (post-cleanup)
  77. 3.4b5 Clean Clean (post-cleanup)
  78. 3.4b6 Clean Clean (post-cleanup)
  79. 3.4b7 Clean Clean (post-cleanup)
  80. 3.4b8 Clean Clean (post-cleanup)
  81. 3.4b9 Clean Clean (post-cleanup)
  82. 3.4b10 Clean Clean (post-cleanup)
  83. 3.4RC1 Clean Clean (post-cleanup)
  84. 3.4 Clean Clean (post-cleanup)
  85. 3.4.1b1 Clean Clean (post-cleanup)
  86. 3.4.1b2 Clean Clean (post-cleanup)
  87. 3.4.1b3 Clean Clean (post-cleanup)
  88. 3.4.1b4 Clean Clean (post-cleanup)
  89. 3.4.1b5 Clean Clean (post-cleanup)
  90. 3.4.1 Clean Clean (post-cleanup)
  91. 3.4.2b1 Clean Clean (post-cleanup)
  92. 3.4.3b1 Clean Clean (post-cleanup)
  93. 3.4.3b2 Clean Clean (post-cleanup)
  94. 3.4.3b3 Clean Clean (post-cleanup)
  95. 3.4.3 Clean Clean (post-cleanup)
  96. 3.4.4b1 Clean Clean (post-cleanup)
  97. 3.4.4b2 Clean Clean (post-cleanup)
  98. 3.4.4b3 Clean Clean (post-cleanup)
  99. 3.4.2 Clean Clean (post-cleanup)
  100. 3.4.4b4 Clean Clean (post-cleanup)
  101. 3.5b4 Clean Clean (post-cleanup)
  102. 3.5b5 Clean Clean (post-cleanup)
  103. 3.5 Clean Clean (post-cleanup)
  104. 3.5.1b1 Clean Clean (post-cleanup)
  105. 3.5.2b1 Clean Clean (post-cleanup)
  106. 3.5.1 Clean Clean (post-cleanup)
  107. 3.5.2b2 Clean Clean (post-cleanup)
  108. 3.5.2 Clean Clean (post-cleanup)
  109. 3.5.3b1 Clean Clean (post-cleanup)
  110. 3.5.3b2 Clean Clean (post-cleanup)
  111. 3.5.2b3 Clean Clean (post-cleanup)
  112. 3.5.3b4 Clean Clean (post-cleanup)
  113. 3.5.3 Clean Clean (post-cleanup)
  114. 3.5.4b2 Clean Clean (post-cleanup)
  115. 3.5.4b3 Clean Clean (post-cleanup)
  116. 3.5.4b4 Clean Clean (post-cleanup)
  117. 3.5.4 Clean Clean (post-cleanup)
  118. 3.5.5 Clean Clean (post-cleanup)
  119. 3.5.6b1 Clean Clean (post-cleanup)
  120. 3.5.6 Clean Clean (post-cleanup)
  121. 3.6b1 Clean Clean (post-cleanup)
  122. 3.6b2 Clean Clean (post-cleanup)
  123. 3.6b3 Clean Clean (post-cleanup)
  124. 3.6b4 Clean Clean (post-cleanup)
  125. 3.6b5 Clean Clean (post-cleanup)
  126. 3.6b6 Clean Clean (post-cleanup)
  127. 4.0b8 Clean Clean (post-cleanup)
  128. 4.0b9 Clean Clean (post-cleanup)
  129. 4.0 Clean Clean (post-cleanup)
  130. 4.0.1b1 Clean Clean (post-cleanup)
  131. 4.0.1b2 Clean Clean (post-cleanup)
  132. 4.0.1b3 Clean Clean (post-cleanup)
  133. 4.0.1 Clean Clean (post-cleanup)
  134. 3.0.2b1 Clean Clean (post-cleanup)
  135. 4.0.2b2 Clean Clean (post-cleanup)
  136. 4.0.2 Clean Clean (post-cleanup)
  137. 4.0.3b1 Clean Clean (post-cleanup)
  138. 4.0.3b2 Clean Clean (post-cleanup)
  139. 4.0.3b4 Clean Clean (post-cleanup)
  140. 4.0.3 Clean Clean (post-cleanup)
  141. 4.0.4b1 Clean Clean (post-cleanup)
  142. 4.0.4b2 Clean Clean (post-cleanup)
  143. 4.0.4b3 Clean Clean (post-cleanup)
  144. 4.0.4b4 Clean Clean (post-cleanup)
  145. 4.0.4b5 Clean Clean (post-cleanup)
  146. 4.0.4 Clean Clean (post-cleanup)
  147. 4.0.5b1 Clean Clean (post-cleanup)
  148. 4.0.5 Clean Clean (post-cleanup)
  149. 4.0.6b1 Clean Clean (post-cleanup)
  150. 4.0.6b2 Clean Clean (post-cleanup)
  151. 4.0.6b3 Clean Clean (post-cleanup)
  152. 4.0.6b4 Clean Clean (post-cleanup)
  153. 4.0.7b1 Clean Clean (post-cleanup)
  154. 4.0.6 Clean Clean (post-cleanup)
  155. 4.0.7 Clean Clean (post-cleanup)
  156. 4.0.8 Clean Clean (post-cleanup)
  157. 4.1 Clean Clean (post-cleanup)
  158. 4.1.1 Clean Clean (post-cleanup)
  159. 4.1.2 Clean Clean (post-cleanup)
  160. 4.2 Clean Clean (post-cleanup)
  161. 4.2.1 Clean Clean (post-cleanup)
  162. 4.2.2 Clean Clean (post-cleanup)
  163. 4.2.3 Clean Clean (post-cleanup)
  164. 4.2.4 Clean Clean (post-cleanup)
  165. 4.2.5 Clean Clean (post-cleanup)
  166. 4.3.1 Clean Clean (post-cleanup)
  167. 4.3.2 Clean Clean (post-cleanup)
  168. 4.3.3 Clean Clean (post-cleanup)
  169. 4.3.4 Clean Clean (post-cleanup)
  170. 4.3.5 Clean Clean (post-cleanup)
  171. 4.3.6 Clean Clean (post-cleanup)
  172. 4.4 Clean Clean (post-cleanup)
  173. 4.2.6 Clean Clean (post-cleanup)
  174. 4.5 Clean Clean (post-cleanup)
  175. 4.6 Clean Clean (post-cleanup)
  176. 5.0.0 Clean Clean (post-cleanup)
  177. 5.0.1 Clean Clean (post-cleanup)
  178. 5.1.0 Clean Clean (post-cleanup)
  179. 5.1.1 Clean Clean (post-cleanup)
  180. 5.1.2 Clean Clean (post-cleanup)
  181. 5.1.3 Clean Clean (post-cleanup)
  182. 5.1.4 Clean Clean (post-cleanup)
  183. 5.1.5 Clean Clean (post-cleanup)
  184. 5.1.6 Clean Clean (post-cleanup)
  185. 5.1.9 Clean Clean (post-cleanup)
  186. 5.1.8 Clean Clean (post-cleanup)
  187. 5.2.0 Clean Clean (post-cleanup)
  188. 5.2.1 Clean Clean (post-cleanup)
  189. 5.2.2 Clean Clean (post-cleanup)
  190. 5.3.0 Clean Clean (post-cleanup)
  191. 5.4.0 Clean Clean (post-cleanup)
  192. 5.5.0 Clean Clean (post-cleanup)
  193. 5.6.0 Clean Clean (post-cleanup)
  194. 5.7.0 Clean Clean (post-cleanup)
  195. 5.8.0 Clean Clean (post-cleanup)
  196. 5.9.0 Clean Clean (post-cleanup)
  197. 5.10.0 Clean Clean (post-cleanup)
  198. 5.10.1 Clean Clean (post-cleanup)
  199. 5.10.2 Clean Clean (post-cleanup)
  200. 3.2 Clean Clean (post-cleanup)
  201. 3.2.2 Clean Clean (post-cleanup)
  202. 3.2.2b1 Clean Clean (post-cleanup)
  203. 3.2.3b3 Clean Clean (post-cleanup)
  204. 3.2.1 Clean Clean (post-cleanup)
  205. 3.2.3b2 Clean Clean (post-cleanup)
  206. 3.2.1b1 Clean Clean (post-cleanup)
  207. 3.2.1b2 Clean Clean (post-cleanup)
  208. 3.2.1b4 Clean Clean (post-cleanup)
  209. 3.2.3b1 Clean Clean (post-cleanup)
  210. 3.2.1b3 Clean Clean (post-cleanup)
  211. 3.2b4 Clean Clean (post-cleanup)
  212. 3.2b3 Clean Clean (post-cleanup)
  213. 5.11.0 Clean Clean (post-cleanup)
  214. 5.12.0 Clean Clean (post-cleanup)
  215. 5.13.0 Clean Clean (post-cleanup)
  216. 5.14.0 Clean Clean (post-cleanup)
  217. 5.15.1 Clean Clean (post-cleanup)
  218. 5.15.2 Clean Clean (post-cleanup)
  219. 5.15.3 Clean Clean (post-cleanup)
  220. 5.16.1 Clean Clean (post-cleanup)
  221. 5.16.0 Clean Clean (post-cleanup)
  222. 5.17.0 Clean Clean (post-cleanup)
  223. 5.18.1 Clean Clean (post-cleanup)
  224. 5.18.2 Clean Clean (post-cleanup)
  225. 5.19.0 Clean Clean (post-cleanup)
  226. 5.20.0 Clean Clean (post-cleanup)
  227. 5.21.0 Clean Clean (post-cleanup)
  228. 5.22.0 Clean Clean (post-cleanup)
  229. 5.23.0 Clean Clean (post-cleanup)
  230. 5.1.7 Clean Clean (post-cleanup)
  231. 5.24.0 Clean Clean (post-cleanup)
  232. 5.25.0 Clean Clean (post-cleanup)
  233. 5.26.0 Clean Clean (post-cleanup)
  234. 5.27.0 Clean Clean (post-cleanup)
  235. 5.27.1 Clean Clean (post-cleanup)
  236. 5.27.2 Clean Clean (post-cleanup)
  237. 5.27.3 Clean Clean (post-cleanup)
  238. 5.27.4 Clean Clean (post-cleanup)
  239. 5.27.5 Clean Clean (post-cleanup)
  240. 5.27.6 Clean Clean (post-cleanup)
  241. 5.27.7 Clean Clean (post-cleanup)
  242. 5.27.8 Clean Clean (post-cleanup)
  243. 5.30.1 Clean Clean (post-cleanup)
  244. 5.30.2 Clean Clean (post-cleanup)
  245. 5.30.3 Clean Clean (post-cleanup)
  246. 5.30.4 Clean Clean (post-cleanup)
  247. 5.30.5 Clean Clean (post-cleanup)
  248. 5.30.6 Clean Clean (post-cleanup)
  249. 5.30.7 Clean Clean (post-cleanup)
  250. 5.30.8 Clean Clean (post-cleanup)
  251. 5.30.9 Clean Clean (post-cleanup)
  252. 5.30.10 Clean Clean (post-cleanup)
  253. 5.30.11 Current Current release

Suspect-shape but structurally unreachable — benign with one regression to flag. YARPP's version_info() matches the high-confidence catalog IOC unserialize_after_remote_call (@unserialize of wp_remote_post body, hardcoded endpoint, sslverify => false, no signature). The exact same shape that made content-egg's Admitad chain a 7-year latent RCE (audit #15). However, two independent failures make the chain dead code today: (1) the type-gate above the unserialize is always-true, blocking the call, and (2) the live yarpp.org/checkversion.php endpoint returns inert HTML rather than serialized PHP. The author is real and the domain is theirs. No active risk; one defensive regression worth flagging to the author.

Plugin

| | | |---|---| | Slug | yet-another-related-posts-plugin (YARPP) | | Author | jeffparker | | Active installs | 100,000 | | Total downloads | 7,960,579 | | Added | 2008-01-02 | | Last update | 2024-11-11 (v5.30.11) | | Closed? | No |

The chain (still in source as of v5.30.11)

classes/YARPP_Core.php:2099-2118:

public function version_info( $enforce_cache = false ) {
    if ( ! $enforce_cache && false !== ( $result = $this->get_transient( 'yarpp_version_info' ) ) ) {
        return $result;
    }

    $version = YARPP_VERSION;
    $remote  = wp_remote_post(
        "https://yarpp.org/checkversion.php?format=php&version={$version}",
        array( 'sslverify' => false )                                                   // ← TLS regression (added 2021-10-12)
    );

    if ( is_wp_error( $remote ) || wp_remote_retrieve_response_code( $remote ) != 200 
         || ! isset( $remote['body'] ) 
         || ! is_array( $remote['body'] ) ) {                                           // ← always-true: gate the unserialize never reaches
        $this->set_transient( 'yarpp_version_info', null, 60 * 60 );
        return false;
    }

    if ( $result = @unserialize( $remote['body'] ) ) {                                  // ← IOC sink, unreachable
        $this->set_transient( 'yarpp_version_info', $result, 60 * 60 * 24 );
    }

    return $result;
}

Three suspicious patterns compounding: 1. Hardcoded HTTP-fetch with sslverify => false — TLS cert validation disabled on transport. 2. responseType=php — explicitly asks for PHP-serialized payload. 3. Error-suppressed @unserialize of remote body, no allowlist, no integrity check.

That's the classic content-egg shape (audit #15). Latent PHP Object Injection if the chain were reachable — any gadget chain (Symfony, Composer, Magento, etc.) becomes RCE on the WordPress site if the operator endpoint or any TLS-MITM path is ever hostile.

Why it's unreachable

Gate failure 1: ! is_array($remote['body']) is always true

wp_remote_post() returns an associative array where body is always a string — that's been WordPress's WP_Http contract since WP 2.7 (2008). So is_array($remote['body']) is false, ! is_array(...) is true, and the early-return on the next line always fires. The @unserialize line is never executed.

$ php -r '$remote = ["body" => "anything"]; var_dump(is_array($remote["body"]));'
bool(false)

This looks like a programmer typo — the author probably intended ! is_array($remote) (defending against null/error responses) but wrote ! is_array($remote['body']), which inverted the meaning and accidentally made the chain dead. Whatever the intent, the practical effect is that version_info() has been silently returning false on every call for ~6 years.

Gate failure 2: live endpoint returns inert HTML

Probed 2026-04-30:

$ curl -X POST "https://yarpp.org/checkversion.php?format=php&version=5.30.11"
HTTP/2 200 
content-type: text/html
content-length: 41

<html><body><h1>200 OK</h1></html></body>

41 bytes of HTML. Even if the is_array gate were fixed, the body isn't serialized PHP — @unserialize($html) returns false, set_transient is never called, function returns false. No exploitation path.

yarpp.org itself 301-redirects to https://yarpp.com/, which serves the active brand site. The checkversion.php URL was preserved (returns harmless 200) presumably for the long tail of installs still polling it.

Git-level forensics

| Date | Tag | Event | |---|---|---| | 2008-01-02 | 1.0 | Plugin shipped — original version_info() already does wp_remote_post(http://yarpp.org/checkversion.php?format=php) + @unserialize($remote['body']). Live exposure begins. | | 2013-12-11 | 4.1-era | Function structure stable; ! isset($remote['body']) early-return added but no is_array typo gate yet. Still exposed. | | ≤ 2020-05-11 | v5.1.4 | Typo gate ! is_array($remote['body']) already present. Chain becomes unreachable at some commit between 2013-12 and 2020-05 (more precise bisection skipped). | | 2020-04-07 | v5.1.3 | URL switched from http:// to https:// (TLS introduced). | | 2021-10-12 | v5.27.6 | Regression: sslverify => false added by jeffparker. TLS cert validation disabled. (Doesn't change the exploit picture because the chain is already unreachable, but it's a defensive regression worth noting.) | | 2026-04-30 | v5.30.11 | Current state — chain unchanged, endpoint returns inert HTML. |

commit fd784c2 (2021-10-12) added sslverify => false to both the version_info() and optin_ping() calls. Likely the author was debugging a cert-chain issue from a host without proper CA bundles and committed the disable as a workaround.

Endpoint ownership

yarpp.orgyarpp.com are both Jeff Parker's domains:

No "dangling-resource takeover" risk on the domain itself.

Threat model retrospective

If the typo gate had not existed (or were "fixed" by a future contributor), the exposure surface would have been:

  • Anyone in a TLS-MITM position (since 2021-10-12, when sslverify => false was added — before that, TLS was honored)
  • yarpp.org's hosting provider, DNS provider, or any future buyer of the domain
  • Anyone who compromised the YARPP server hosting checkversion.php

No public reports of in-the-wild exploitation. The gate has effectively neutralized the chain since v5.1.4 (May 2020) at the latest, and likely earlier.

Author intent assessment

jeffparker is the verified author of record (committer since 2013-09, member since [pre-2013]), with mitchoyoshitaka (Mitcho Erlewine, original co-author) as the prior committer 2012-2013, and mnelson4 as a brief 2021 contributor. No supply-chain-style changes — only routine maintenance. The code architecture is consistent with a 2008-era WordPress plugin that grew incrementally without a security retrofit. format=php was a standard PHP idiom of the era; the typo gate looks like an accidental defense rather than intentional gating.

Verdict

Benign — historical exposure (now closed). Same disposition as audit #15 (content-egg): suspect-shape chain matching a known IOC, with two independent failures making it unreachable. Difference from #15: YARPP's "gating" is an accidental typo rather than a deliberate throw, so the unreachability is fragile — a future contributor who "fixes" the always-true check could silently re-enable the sink.

Recommendations

To author (jeffparker): in priority order:

1. Switch the response format off ?format=php. Either change to ?format=json and use json_decode( wp_remote_retrieve_body($remote) ), or drop the version-check entirely (WordPress's own update API now handles this). Removes the IOC entirely. 2. Revert the sslverify => false regression added in v5.27.6 (commit fd784c2, 2021-10-12). If there's a TLS reason it was added, the right fix is updating the local CA bundle, not disabling validation. 3. Fix the always-true ! is_array($remote['body']) gate — but only after doing #1, otherwise this fix re-enables the IOC sink. The intent-likely guard is ! is_array($remote).

Cleanup status

cleanup_status = clean — no live exposure, no remediation needed for site owners. Author should land #1 + #2 above for hygiene; site owners running YARPP today are not at risk from this chain.