How to take screenshot of url with PHP

How to take screenshot of url with PHP

Here is an example and source code of how to take screenshot of url with php. I have taken this code from one of my projects. It is also able to handle https and optionally, follow through with redirects.

Requirements :
-PhantomJS standalone executable(.exe)
-PHP library for phantomjs

Setup :

  1. Follow the installation for PHP PhantomJS here
  2. Set the path to your phantomjs executable file in the code

    $client->getEngine()->setPath(__DIR__.’/bin/phantomjs.exe’);

    Note : You may have to rename /bin/phantomjs to /bin/phantomjs.exe

Additional features :
– customize width/height of screenshot
– support http and https
– follow redirects

 

require 'vendor/autoload.php';

use JonnyW\PhantomJs\Client;


function get_redirect_url($url)
{
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_HEADER, true);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Set to true to make curl follow the url
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	$a = curl_exec($ch); 
	$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); // This gets the last location/url after all the redirection has been followed through

	return $url;
}
function get_http_response_code($url)
{
	$ch = curl_init($url);
	curl_exec($ch);
	$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
	return $code;
}
function is_redirect($url) 
{
	$code = get_http_response_code($url);
	if (($code == 301) || ($code == 302)) {
		return true;
	}
	return false;
}



function screenshot($width, $height, $url, $filePath="file.jpg", $followRedirect=false)
{
        $path = pathinfo($filePath);
        $directory = $path['dirname'];

	if( $followRedirect && is_redirect($url) ) 
	{
		$redirectedUrl = get_redirect_url($url);
		echo "{$url} redirects to {$redirectedUrl} <br/>";
		$url = $redirectedUrl;
	}		

	// Ensure the path is writable to PhantomJS/PHP
	if( !is_writable($filePath) )
	{
		//echo "$directory of $filePath is not writable. Chmod-ing <br />";
		//return false;
	}

	$client = Client::getInstance();
	$client->getEngine()->setPath(__DIR__.'/bin/phantomjs.exe');
	// HTTPS support
	$client->getEngine()->addOption('--ssl-protocol=any');
	$client->getEngine()->addOption('--ignore-ssl-errors=true');
	$client->getEngine()->addOption('--web-security=false');

	$width  = $width ? $width : 1280;  // Note : Specifying a 800x600 may be too small and result in @media css determining
	$height = $height ? $height : 800; // it to be a mobile instead of desktop. Generally a 1280x800 should be good.
	$top    = 0;
	$left   = 0;


	//@see JonnyW\PhantomJs\Http\CaptureRequest
	$request = $client->getMessageFactory()->createCaptureRequest($url, "GET"); //('http://jonnyw.me', 'GET');
	$request->setOutputFile($filePath);
	$request->setViewportSize($width, $height);
	$request->setCaptureDimensions($width, $height, $top, $left);
	$request->setDelay(3); // Depends on how long it takes for the webpage to fully load. this vs setTimeout(x) ?
	$request->setTimeout(1000); // Not sure the diff between setDelay and setTimeout though...

	//@see JonnyW\PhantomJs\Http\Response 
	$response = $client->getMessageFactory()->createResponse();

	// Send the request
	$client->send($request, $response);
	
	return file_exists($filePath); // Check if screenshot exists
}

 

Enjoyed the content ? Share it with your friends !

Python subdomain bruteforce script

Python subdomain bruteforce script

This script basically tries to bruteforce the subdomains of a given domain name. It includes various options such has min and max length, avoiding scanning when certain character are detected and so on.

Its currently single threaded so it might take awhile when its more than 6 characters. Will try to work on it when i have the time to do so

It is available on github here

Enjoyed the content ? Share it with your friends !

Assembly language crash course

Assembly language crash course (Intel x86 Architecture)

The goal of this assembly language crash course is to help you have a quick overview of the assembly language, and transition from a higher-level language to a lower-level one (in this case, it’s assembly). Therefore, it is imperative that you already possess a programming background in order to be understand my explanations or references later on.

A good habit to have when learning assembly language is to convert these low level codes into that of a high level language to better understand the flow, logic and code.

Now, on to the actual assembly language. There are 3 main components that you should take note of when you first begin learning. Namely – Registers, Flags, and the Code itself.

Registers are like global variables which can hold 4 bytes (32 bits). Registers can be used for anything – for calculation, for copying of data, … There are however, standards or common usage for each register though.  There are 8 of these registers – namely,

Eax, Ecx, Ebx, Edx – General purpose registers

Esi, Edi – Index register

Ebp – Pointer to base of current stack frame

Esp – Pointer to the top of the stack

Flags are well, flags … also known as EFLAGS (x86 system) or RFLAGS (x64 system). Like your normal programming flags, they are binary values – either 0 or 1 and are used to signify that an event has happened, which can can be used as part of your code’s decision-making logic.

Zero Flag (ZF) – If an operation has a result of 0, the ZF is set to 1. This flag is checked by conditional jump opcodes(the equivalent of ‘if’, ‘else’, ‘switch’ ..)

Overflow Flag (OF) –  indicate when an arithmetic overflow happened in an operation

Sign Flag (SF) – indicate whether the last mathematical operation resulted in a value in which the most significant bit was set. Or if you interpret the result as two’s complement, the negative flag will be set if the result was negative.

Codes are as you guessed it – codes. One line of assembly code is usually made up of two components – an opcode and operand. An opcode is usually the prefix (or the code at the start of the line) and operand is everything else. Take a look at the example below (It’s alright if you don’t understand it yet)

mov eax, edx // mov is the opcode, eax & edx are operands.

sub ebx, edi// sub is the opcode, ebx & edi are operands

 

Now that you know what Registers, Flags, and Codes are, you will now need to know how assembly does its stuff – calculations, copying of data, calling a function and so on. There will be 3 parts to this.

  1. Data manipulation (calculation, copying of data..)

    mov eax, edx // edx = eax; move the value from edx into eax

    sub eax, edx // eax = eax – edx; // subtract edx with eax, result is in eax

    add eax, edx // eax= eax + edx; // add edx with eax, result is in eax

    note that while i used registers ‘eax’ and ‘edx’ above, they can also be replaced with a hard coded value. For example,

    mov eax, 5

    sub eax, 9

    add eax, A // note : a constant value is usually denoted in hex

  2. Comparing of values
    Now that you know how to do simple calculations in assembly, let’s move on to comparing of values in assembly. This is where the Zero Flag that I have mentioned earlier will come into use.

    cmp eax, edx // if ( eax==edx ) ZF = 1;

    On the surface it may be just comparing values, but underneath it, it does a subtraction of eax and edx. So if eax-edx == 0, the ZF is set to 1. The result of the subtraction is not stored anywhere.

    test eax, eax // if ( eax==0 ) ZF = 1;

    For the ‘test’ opcode, it does not do a subtraction, it instead does a bitwise AND. If the result of the bitwise AND operation is 0, the ZF is set to 1. In code it would look like if( (eax AND eax) == 0 ) ZF=1;

  3. Function calls & conditional jumps
    Now that you know how CMP and TEST works, all that is left are the basic conditional jumps & calls

    JMP location // unconditional jump – Jump regardless of what happens

    JZ location // ‘jump if zero flag set’ – condition ZF=1

    JNZ location // ‘jump if not zero flag set’ – condition ZF=0

    JLE location // ‘jump if less than or equal’ – condition ZF = 1 or SF <> OF

    JG location // ‘jump if greater’ – condition ZF = 0 and SF = OF

    CALL location // nothing special. same as normal programming calls like foobar();

Now that you have a basic grasp of the assembly language, next thing you should do is to practise it ! Be it through programming or using assembly to reverse engineer – practise makes perfect.

 

Enjoyed the content ? Share it with your friends !

DataTables filter by ip address

DataTables filter by ip address

I had to make a custom filter for ip address which accepted cidr notation (e.g /24). While there was the function to sort by ip at the time when i was doing it, a filter by ip address that accepted cidr or subnet masks function didn’t seem to have been implemented. I couldn’t find any publicly available solution online either, so i took some time to implement it. The solution is in php but can be easily ported to javascript/html if you wish

To get it to work on your own code, you will have to modify the 2 variables

var ipColNum = 0; // column number of your ip address

var tableId = “#results”; // table id that you want to attach datatables to

<!--jquery-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<!--datatables-->
<link rel='stylesheet' type='text/css' href='//cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css'/>
<script type='text/javascript' src='//cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js'></script>


<table id="results">
  <thead>
    <tr>
      <th>Ip Address</th>
      <th>Name</th>
      <th>Country</th>
    </tr>
  </thead>
  
  <tbody>
    <tr>
      <td>192.168.0.1</td>
      <td>abc</td>
      <td>abc country</td>
    </tr>
    <tr>
      <td>122.20.21.133</td>
      <td>xyz</td>
      <td>xyz country</td>
    </tr>
    <tr>
      <td>192.168.0.10</td>
      <td>john</td>
      <td>john country</td>
    </tr>
    <tr>
      <td>192.168.1.1</td>
      <td>sally</td>
      <td>sally country</td>
    </tr>
  </tbody>
</table>


<script>
    var table = null;
    var ipColNum = 0; // set the column number for the ip address
    var tableId = "#results";
    
    function getIpRangeFromAddressAndNetmask(str) {
      var part = str.split("/"); // part[0] = base address, part[1] = netmask
      var ipaddress = part[0].split('.');
      var netmaskblocks = ["0","0","0","0"];
      if(!/\d+\.\d+\.\d+\.\d+/.test(part[1])) {
        // part[1] has to be between 0 and 32
        netmaskblocks = ("1".repeat(parseInt(part[1], 10)) + "0".repeat(32-parseInt(part[1], 10))).match(/.{1,8}/g);
        netmaskblocks = netmaskblocks.map(function(el) { return parseInt(el, 2); });
      } else {
        // xxx.xxx.xxx.xxx
        netmaskblocks = part[1].split('.').map(function(el) { return parseInt(el, 10) });
      }
      var invertedNetmaskblocks = netmaskblocks.map(function(el) { return el ^ 255; });
      var baseAddress = ipaddress.map(function(block, idx) { return block & netmaskblocks[idx]; });
      var broadcastaddress = ipaddress.map(function(block, idx) { return block | invertedNetmaskblocks[idx]; });
      return [baseAddress.join('.'), broadcastaddress.join('.')];
    }

  function ip2long(IP) {
    var i = 0;
    IP = IP.match( /^([1-9]\d*|0[0-7]*|0x[\da-f]+)(?:\.([1-9]\d*|0[0-7]*|0x[\da-f]+))?(?:\.([1-9]\d*|0[0-7]*|0x[\da-f]+))?(?:\.([1-9]\d*|0[0-7]*|0x[\da-f]+))?$/i );
    if (!IP) { return false; }
    IP[0] = 0;
    for (i = 1; i < 5; i += 1) {
      IP[0] += !!((IP[i] || '').length);
      IP[i] = parseInt(IP[i]) || 0;
    }
    IP.push(256, 256, 256, 256);
    IP[4 + IP[0]] *= Math.pow(256, 4 - IP[0]);
    if (IP[1] >= IP[5] || IP[2] >= IP[6] || IP[3] >= IP[7] || IP[4] >= IP[8]) { return false; }
    return IP[1] * (IP[0] === 1 || 16777216) + IP[2] * (IP[0] <= 2 || 65536) + IP[3] * (IP[0] <= 3 || 256) + IP[4] * 1;
  }

//https://www.jqueryscript.net/demo/DataTables-Jquery-Table-Plugin/examples/plug-ins/range_filtering.html
var ipFilter = function(searchValue) {
    $.fn.dataTable.ext.search.push(
        function( settings, data, dataIndex ) {
            console.log("srchValue" + searchValue);
            var ipRange = searchValue;
            var colIpAddr = data[0]; // use data for the age column
     				var range = getIpRangeFromAddressAndNetmask(ipRange);
            var min = ip2long(range[0]);
            var max = ip2long(range[1]);

            //console.log(ipRange);
            //console.log("min : " + min + " max : " + max);
            //console.log("chk : " + colIpAddr);

            var longIpAddr = ip2long(colIpAddr);
            
            if( longIpAddr >= min && longIpAddr <= max ) {
                return true;
            }
            return false;
        }
    );

    table.draw();
    $.fn.dataTable.ext.search.pop();
};

 

$(document).ready( function () {
    table = $(tableId).DataTable({
        responsive: true
    });

    if( !table ) {
        console.log("Failed to initialize DataTable()");
    } else {
   
        // Change filter algorithm when cidr notation detected
        $('.dataTables_filter input').unbind().keyup(function() 
        {
            var value = $(this).val();
            if (value.length==0) {
                table.search('').draw();
                return;
            }

            // Must have the format xxx.xxx.xxx.xxx/0-32 
            // Does not match any string without a '/<number>' at the back.
            if( /^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))$/.test(value) ) {
                // We need to reset the table first, otherwise our filter won't work 
                // probably because it detects e.g 192.168.0.1/32 -> 192.168.0.1, 192.168.0.1/ , 192.168.0.1/3, 192.168.0.1/32. The earlier 2 has to results.
       
                table.search('').draw();
                ipFilter(value);
            } else {
                table.search(value).draw();
            }
        });
        
    }
} );


</script>

 

See it in action over at the JsFiddle : here or below .You can test it out by putting a search value of “192.168.0.0/23” or any others of your choice

Enjoyed the content ? Share it with your friends !

Python pingsweep script

Python pingsweep script

This python pingsweep script basically does a ping sweep over a specified ip range (with cidr notation) and generates the output in a .txt file . it should work on both python2+ and python3+

Output will give the following fields

  • ip address
  • reverse nslookup for domain name
  • get title of website if any
  • get http code

It currently is only able to do /24 scans (256 ips).If i have time in the future i will work on it to accept any ip range, and hopefully make it multi-threaded so that scans don’t take too low.

The code may be outdated. So best to refer to github link below

# ping sweep
# firstly, we ping to see if the server at ip is alive
# secondly, we test to see if it responds to http request(80)

import subprocess
import os
import urllib.request
import socket


def log_ping(file_name, msg):
    with open(file_name, "a") as ipFile:
        ipFile.write(msg + "\n")

def http_ping(ip):
    try:
        response = urllib.request.urlopen("http://" + ip).getcode()
        return response
    except:
        return 0
def rdns_lookup(ip):
    try:
        return socket.gethostbyaddr(ip)
    except socket.error:
        return "<couldnt get domain name>"
    
def get_html_title(ip):
    webpage = urllib.request.urlopen("http://" + ip).read()
    html = str(webpage)
    if "<title>" in html: #sometimes the document doesnt have a title tag
        title = html.split('<title>')[1].split('</title>')[0]
        return title
    return "<cant parse title>"
    
    
ip = input("Enter a /24 in the format e.g 10.21.32. : ")	
if ip.count('.') != 3:
    input("IP Format is wrong. Please restart and try again ")
    quit()
        
file_name = ip + "0.txt"
log_ping(file_name, "The format is as follow - <ip address>, <active>, <http response code>, <html title>")
    
with open(os.devnull, "wb") as limbo:
    for n in range(0, 256):
        scan_ip = ip + str(n) 
        result=subprocess.Popen(["ping", "-n", "1", "-w", "200", scan_ip],
        stdout=limbo, stderr=limbo).wait()
        if result:
            msg = scan_ip + " inactive"
            print(msg)
            log_ping(file_name, msg)
        else:
            response = http_ping(scan_ip)
            title = ""
            if response == 200:
                title = get_html_title(scan_ip)
                    
            domain = rdns_lookup(scan_ip)
            
            msg = scan_ip + "({0})".format(domain) + " active, " + str(response) + " , " + title
            print(msg)
            log_ping(file_name, msg)

Github link : here

 

 

Enjoyed the content ? Share it with your friends !