One of the interesting challenges of propagating malware through social media is the requirement of obtaining new accounts for spamming. The Koobface botnet automates this process by leveraging zombies to sign up for accounts on Facebook, Gmail, Blogger, and Google Reader. Each of these services requires a solution to a Captcha challenge which Koobface pushes off on a zombie machine’s owner to solve. During the course of infection, a Koobface zombie will repeatedly poll the C&C for Captchas requiring solutions, in turn prompting users with a threat to shutdown unless the Captcha is solved. As any zombie can query the botnet for a Captcha solution, an adversary can re-purpose the botnet into a personal Captcha solver.
Initiating a Request
A Captcha request to the system can target either Google’s Captcha software or reCAPTCHA; in the case of Google, a flag &b=goo is appended to the request. As with most traffic between a zombie and the Koobface C&C, traffic occurs in plaintext over HTTP. Solving a Captcha is initiated by sending an HTTP POST to a Koobface C&C server. The HTTP POST includes the raw JPG data for the Captcha image to be solved, which will be displayed on a victim’s machine.
The Koobface server will return a random identifier that’s appended in future requests to the server as a zombie repeatedly probes for a response:
For each solution request, the server will respond with one of three states, directing the bot to its appropriate behaviour:
1 | Pending; repeat request at later time 3 | Solution available 0 | Abandon current request; no one has solved in maximum timeout
Once injected, a separate zombie will randomly probe for a Captcha requiring a solution and submit a victim’s response to the C&C. The zombie also reports its version [&v=20], and the number of attempts its made contacting the C&C for a Captcha [&i=0; i++ for each failed attempt].
# Request a Captcha ID requiring a solution. GET /.sys/?action=captcha&a=get&i=0&v=20 # Request the image associated with a given ID GET /.sys/?action=captcha&a=image&i=3&v=20&id=23063812 # Upload a response GET /.sys/?action=captcha&a=put&id=23063812&v=20&code=valid%20text
Use of the goo flag when injecting a packet is necessary as victims are prompted with different instructions depending on the type of Captcha being solved. Given the wrong flag, a valid solution wont be accepted due to a regular expression mismatch; Google has only one word in its Captchas, while reCATPCHA uses two words.
Enter both words below, separated by a space.| ([a-zA-Z0-9\$\.\,\/]+)([ ]+)([a-zA-Z0-9\$\.\,\/]+) Enter the word below.| ([a-zA-Z0-9\$\.\,\/]+)
def send_packet(domain,packet): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((domain, 80)) s.send(packet) resp = s.recv(1024) return resp except (socket.error,socket.herror,socket.gaierror,socket.timeout): logging.error("Failed to query socket") return None def captcha_query(): # Select a random C&C domain from list of Koobface hosts domain = random.choice(CC_DOMAINS) domain = re.sub('(http://)?(www\.)?','',domain) # Fetch a Captcha image to be solved data = fetch_image() length = len(data) # Mimic Koobface's zombie behavior packet = "POST /.sys/?post=true&path=captcha&a=save&b=goo HTTP/1.0\r\n" + \ "accept-encoding: text/html, text/plain\r\nConnection: close\r\n" + \ "Host: %s\r\nUser-Agent: Mozilla/5.01 " %(domain)+ \ "(Windows; U; Windows NT 5.2; ru; rv:220.127.116.11) " + \ "Gecko/20050104 Firefox/3.0.2\r\n" + \ "Content-Type: binary/octet-stream\r\n" + \ "Content-Length: %s\r\n\r\n" %(length) packet = str(packet) + str(data) # Sent packet using raw socket resp = send_packet(domain,packet)