Gaming foursquare with 9 lines of Perl
by Mayank Lahiri, August 18th, 2010Click here for updates.
Foursquare (and its competitors GoWalla and Facebook Places) is an online service that allows you to use you fancy modern smartphone to tell the whole world exactly where you are right now. The New York Times carried an impassioned article today about how these 'checkin services' are increasingly patronized by young people as they scramble to be awarded virtual 'mayorships' of businesses, buildings, and even lost alleyways.
On this page, I pose the following question: how many lines of code does it take to game Foursquare and become the virtual mayor of a place using nothing but Perl scripts?
This is merely for research purposes. As long as there are at least a few businesses that offer monetary rewards to virtual 'mayors', there exists the incentive to game the system. Learning how easy it is will hopefully offer some perspective on this trend.
I demonstrate how to do it in 9 Perl statements. This is not particularly surprising to anyone who understands TCP. Further contributions that satisfy the requirements below in fewer statements are welcomed, and will be appended to this page with attribution.
Requirements
- No non-standard libraries.
- Must accept a venue ID and base GPS coordinates as command line input.
- Must randomly perturb GPS coordinates a little each time.
- Must sleep for a random period of time after initiation.
- Must check-in to the specified location.
In 9 Perl statements
Note that in the code below, I've replaced my user id and password with the string XXXXXX. To use this script, you'll have to replace it with the Base64 encoding of "email/phone:password".#!/usr/bin/perl -W use IO::Socket; srand; sleep(rand()*600); my $sock = IO::Socket::INET->new(PeerAddr=>'api.foursquare.com', PeerPort=>80, Proto =>'tcp', Type=>SOCK_STREAM) or die; $ARGV[1] += rand() * 0.0001 - 0.00005; $ARGV[2] += rand() * 0.0001 - 0.00005; my $str = "vid=$ARGV[0]&private=0&geolat=$ARGV[1]&geolong=$ARGV[2]"; print $sock "POST /v1/checkin HTTP/1.1\r\nHost: api.foursquare.com\r\nUser-Agent:" ." Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ " ."(KHTML, like Gecko) Version/3.0 Mobile/1C10 Safari/419.3\r\nContent" ."-Type: application/x-www-form-urlencoded\r\nAuthorization: Basic " ."XXXXXX\r\nContent-length: ", length($str)+2, "\r\n\r\n$str\r\n"; $_=<$sock>;
NOTE: To get this script to work, you must replace XXXXXX with the Base64 encoded version of "email/phone:password", so base64("john@doe.com:mypassword"). Here's Google's top ranked site for online Base64 encoding.
Staging a foursquare coup
Ever since I was a wee lad, my dream has always been to be the foursquare mayor of a number of places here in San Francisco, CA:- The Revolution Cafe, a small bar in the Mission with live music
- Bourbon and Branch, a great speakeasy in the Tenderloin district
- The Civic Center BART stop, for no good reason
Weekdays:
Revolution Cafe at 1pm every weekday, because I'm a regular.
Bourbon and Branch immediately afterwards at 3:30pm every weekday.
Civic Center BART at 4:00pm every weekday, while barhopping.
Weekends:
Revolution Cafe at 4pm.
Bourbon and Branch at 8pm.
Please don't game my locations! :)
Why checkins will be vulnerable for quite some time
Foursquare could enforce travel times based on physical distance between checkins.OK, this just slows down our rate of checkins and doesn't fix the problem. Also, I own a Ferrari.
Foursquare could use GeoIPs to make sure that reported locations are approximately correct.
Two ways around this: phone apps that exist solely to send these bogus checkins from mobile towers
with presumably different IPs, or for the determined coup planner, a set of proxy servers (or
friends' computers) around the city. The granularity of GeoIP isn't very fine, so this makes the
job much easier.
Foursquare could block checkins for geographically different locations from the same IP
Still susceptible to localized proxies (or web hosting), using Tor, or to using a laptop and moving around a bit. This would also exclude at least some people who tunnel through VPNs.
Foursquare could attempt to group IPs that are geographically "near" a checkin spot.
Impossible to do this without denying service to a brand new IP that appears in the area (say due to an ISP reconfiguration).
In other words, it's very difficult, if not impossible, to tell a bogus 'checkin' from a real one. My prediction is that the incentive offered by businesses to virtual 'mayors' will never increase beyond an insignificant amount, because the incentive to game the 'mayor' system would become too high.
Disclaimer: This was done on my own time, for research purposes only, and is meant as an academic curiosity.
Updates
August 21, 2010: This got posted on Slashdot. Here's the story. The Slashdot effect sent at least 13,141 unique visitors over this weekend, which bumped up the traffic to my tiny homepage by 9,025%. And that's not even counting users who have disabled Google Analytics.August 22, 2010: Eli Foley sent in this 2-line Ruby script that exploits the OO nature of Ruby (I'm presuming) to chain functions into one long logical statement. Since this isn't a serious "contest" anyway, it's allowed!
Eli writes: "would have been prettier with Net/http, but since i don't use four-square i wanted to keep it as close to yours as possible."
puts TCPSocket.open('api.foursquare.com', 80).print("POST /v1/checkin HTTP/1.1\r\nHost: api.foursquare.com\r\nUser-Agent: Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1C10 Safari/419.3\r\nContent-Type: application/x-www-form-urlencoded\r\nAuthorization: Basic XXXXXX\r\nContent-length: #{(m = "vid=#{ARGV[0]}&private=0&geolat=#{ARGV[1].to_i + rand(100)*0.01 - 0.00005}&geolong=#{ARGV[2].to_i + rand(100)*0.01 - 0.00005}").length+2 }\r\n\r\n#{m}\r\n").read if sleep(rand(100)*6)
August 23, 2010: Kevin LeCureux writes in with this interesting idea:
August 23, 2010: Some interesting comments about this on Hacker News, Martin Kou's blog, and Reddit.
August 23, 2010: The Buzz Media has a nice writeup explaining how to use this hack with lots more detail. Thanks for the attribution, Riyad! And his tutorial seems to have made it to dzone, where they say: "Not typically a DZone story (social media hype) but the hack (mock HTTP POST) is too entertaining to not share." Indeed! :)
August 25, 2010: Alex Ford sends in this two logical line Perl nugget, in the truest Perl readability style (it's three lines if you count the MIME::Base64 import). And we wonder why people complain about Perl! :)
!/usr/bin/perl -w use MIME::Base64; use IO::Socket; readline do { $sock if $sock = IO::Socket::INET->new(PeerAddr => 'api.foursqure.com', PeerPort => 80, Proto => 'tcp', Type => SOCK_STREAM) and printf($sock join(v13.10, "POST /v1/checkin HTTP/1.1", "Host: api.foursquare.com", "User-Agent: Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) " . "AppleWebKit/420+ (KHTML, like Gecko) " . "Version/3.0 Mobile/1C10 Safari/419.3", "Content-Type: application/x-www-form-urlencoded", "Authorization: Basic ${\encode_base64('YOUR_USERNAME_HERE' . ':' . 'YOUR_PASSWORD_HERE')}", 'Content-length: %2$s', '', '%1$s' ), local $_ = sprintf('vid=%s&private=0&geolat=%s&geolong=%s', $ARGV[0], map { $_ + rand() * 1e-4 - 5e-5 } @ARGV[1, 2]), 2 + length) } if sleep rand() * 600;
August 26, 2010: Thaddeus Bogner suggests using a CAPTCHA for each checking, but we both agree that it would probably be ineffective or ruin the user experience.
August 29, 2010: Rodrigo from Madrid, Spain sends in this cool little meta-hack. "It's a little pun on the concept of gaming Foursquare: this time,it's your site that has been gamed, which in turn games Foursquare."
use IO::Socket; $sock0 = IO::Socket::INET->new(PeerAddr=>'compbio.cs.uic.edu', PeerPort=>80, Proto =>'tcp', Type=>SOCK_STREAM) or die; print $sock0 "GET /~mayank/4sq.html HTTP/1.0\n\n"; $/=(); ($d) = <$sock0> =~ m[<pre>(.*?)</pre>]gs; $d =~ s/XXXXXX/YYYYYY/g; eval $d;