How to Hack taaalk.co

I'm the founder of Taaalk ✌️
I'm a security engineer at Stripe and former 2200 FIDE chess player. I write about programming, security, and a few other things at robertheaton.com.
Follow this Taaalk

3 followers

1,485 views

Joshua Summers
12:17, 11 May 20 (edit: 13:34, 11 May 20)
It all started with an 'F'.
I had given you (Robert) access to my code base to check for vulnerabilities. After that I noticed that something small had changed. The Taaalk I created to discuss feedback "Taaalk Feedback" had become "Taaalk feedback". Capital F to lower case f.

Screenshot 2020-05-11 at 14.33.52.png 23.8 KB

In theory, only the person who started the Taaalk can change it's title, but you had done it without my permission.
When you looked at my code base, how did you know you could get away with that?
Robert Heaton
13:46, 11 May 20
I noticed that the routes in your Rails controllers didn't have any systematic authorization. This meant that there was no consistent enforcement of who was allowed to perform a particular action. The code was diligently ensuring that requests come from a valid, authenticated user, but was not always checking what the user should and shouldn't be allowed to do.
I noticed that one place in which no authorization checks were being performed was the route in which a Taaalk gets updated:
I decided to have a prod.
Joshua Summers
16:03, 11 May 20
Right, so the first place you look to see if there are any vulnerabilities are various controllers?
Right now my code for updating is:
def update
    @tlk = Tlk.includes(:spkrs, :msgs).friendly.find(params[:id])
    @tlk.update(tlk_params)
    respond_to do |format|
      format.js { render 'update', layout: false } # { @tlk }# <-- will render `app/views/reviews/create.js.erb`
      format.html { redirect_to new_tlk_path(@tlk) }
    end
end
Should I be building my own validator method? For example:
if @tlk.user == current_user do
...
end
Or is systematic authorisation something that should be checked elsewhere in a more formal way?
And beyond that, how do you actually begin sending a request to update my Taaalk? One of the mysteries I have had around 'hacking' a site is the actual steps required to hack. What did you literally do to change the title of the feedback Taaalk?
Robert Heaton
21:28, 11 May 20
I started by looking at the controllers because that's the code that it executed when the application receives a request. I would usually focus on the funky functionality that might have interesting bugs (eg. invite codes), but I noticed that there was nothing stopping me from updating Taaalks that I didn't own and wasn't even participating in.
To edit the main Feedback Taaalk I used a "proxy" called Burp Suite. You route all the HTTP traffic from your browser through Burp, where you can inspect and edit it. I clicked the button to edit a Taaalk that I owned, changed the Taaalk ID in the request to my target Taaalk, then forwarded the request on to the server.
I would recommend using a systematic authorization framework like https://github.com/CanCanCommunity/cancancan . This allows you to see at a glance what different types or use can and can't do. If the current user isn't authorized to perform an action then the controller code ideally shouldn't even be reached. This gives you fewer opportunities in the future to ruin everything by accidentally deleting an if-statement.
Joshua Summers
12:35, 12 May 20
Haha, yeah I had thought about some of those issues, especially around message creation. When developing Taaalk I realised that if I edited the HTML of the message box form I could post a message into any Taaalk - even one I wasn't a member of! I solved that, but again in a custom way.
I have a few questions in my head, firstly if you were building an application, is there a kind of systematic thinking that you would go through to make things as tight as can be? 
And secondly, something I know I'm guilty of, when do you look for an external solution to a problem? I think because of my limited gem experience, my gut is to build if I can thinking of a logical way to solve the problem - for example:
if @tlk.user == current_user do
...
end
But perhaps I'm thinking too immediately, as in - if I can solve the problem in front of me right now, that's good - and not thinking about problem in a wider application wide context. Is that something that you feel has shifted in your thinking as you've gained experience?
Robert Heaton
16:32, 12 May 20
A good place to start is the OWASP Top 10 Web Application Security Risks - https://owasp.org/www-project-top-ten/ . You should consider whether the code your writing is affected by any of these risks. Displaying user input? Think about XSS. Handling private data? Think about how you will prevent the wrong people from seeing it.
It's also possible to write your code in such a way that it is harder to accidentally introduce bugs. For example, in general user input is unsanitized by default and you have to remember to sanitize it yourself or you're screwed. You may be able to flip this so that all your user input is sanitized by default, and if you knowingly want to do something dangerous then you have to opt in to doing so. The excellent book "Secure by Design" talks about this in detail - https://www.manning.com/books/secure-by-design .
Whether to use a library or a one-off solution is an eternal debate with no right answer. Something like authentication is so core and important that I think you should almost always use something systematic like CanCan (see above). If you sprinkle lots of statements like `if @tlk.user == current_user` around your code then it's hard to see at a glance which endpoints are properly secured. Centralizing your authorization logic gives you a single place to check what different types of user can do. For example, from the CanCan docs:
class Ability
  include CanCan::Ability

  def initialize(user)
    can :read, Post, public: true

    if user.present?  # additional permissions for logged in users (they can read their own posts)
      can :read, Post, user_id: user.id

      if user.admin?  # additional permissions for administrators
        can :read, Post
      end
    end
  end
end
If you have a small application then you can probably get away with simple, inline checks. As it gets larger you'll want something more systematic.
Joshua Summers
11:15, 18 May 20 (edit: 11:45, 18 May 20)
In a Rails application there is a file responsible for hiding/showing parameters in HTTP requests: 'config/initializers/filter_parameter_logging.rb'
The default setup is simply:
Rails.application.config.filter_parameters += [:password]
If I were to add, say, 'taaalk_id' to this list of filters, would I be able to stop your attack from being successful?
Robert Heaton
19:05, 18 May 20
Nope, that wouldn't help. `filter_parameters` controls which information is displayed in your logs, it doesn't have any effect on your application logic. In general you backend server won't be able to know whether someone has messed with a request (or even whether the request was made by an actual web browser), so the only way to solve your problem is to add additional logic that explicitly says which people are allowed to take which actions.
Joshua Summers
19:18, 18 May 20
Oh right. I didn't realise that was just my server side logging.
So everything I send over an HTML request is visible and malleable? 
Follow this Taaalk

3 followers

1,485 views

Start your own Taaalk, leave your details or meet someone to Taaalk with.