DEV Community

Vay3t
Vay3t

Posted on • Originally published at vay3t.Medium

Creando un notificador en Telegram con Bash

Image description

¿En que consistirá el notificador?

Crearemos una herramienta la cual enviará un mensaje a un bot de Telegram creado y configurado por nosotros, de esta forma nos notificará ante algún evento que nosotros hallamos programado.

Se creará una herramienta en dos modalidades, como función para usarlo en ~/.bashrc y como herramienta de línea de comandos que estará ubicado en /usr/bin/.

Creando y configurando el bot con BotFather

Antes que nada necesitamos buscar el bot BotFather que nos ayudará a crear y administrar nuestros bots dándonos un token para poder usarlos.

Image description

  • En el bot se pueden usar las siguientes opciones:

Image description

Para añadir nuestro bot tenemos que usar el comando /newbot, luego colocarle un título y un nombre de usuario para poder identificarlo entre todos los usuarios y bots existentes. Luego de eso nos dará el link para acceder al chat del bot y un API token para trabajar e interactuar con él.

Image description

Programando el bot

Ya creado el bot, accedemos a su chat desde el link entregado luego de configurarlo y damos a /start.

Image description

Para hacer funcionar la herramienta es necesario saber nuestro id de chat (id de usuario de Telegram) que sirve para poder identificar qué usuario está interactuando con el bot. Para eso enviamos un mensaje de prueba al bot.

Image description

A continuación debemos obtener id del chat, con el siguiente link:

https://api.telegram.org/bot<YourBOTToken>/getUpdates
Enter fullscreen mode Exit fullscreen mode

Este es un ejemplo de cómo obtener el dato necesario usando curl:

curl https://api.telegram.org/bot1318386267:AAHOq8X5lqpjkWfPnXJh3etK8JyDKt1YNCI/getUpdates -s | jq
Enter fullscreen mode Exit fullscreen mode

El campo id es nuestro identificador de chat, que será necesario para que nos lleguen las notificaciones a nuestra cuenta.

Image description

  • Este trozo de código tiene que ser añadido al final del archivo ~/.bashrc:
function urlencode() {
        # urlencode <string>
        old_lc_collate=$LC_COLLATE
        LC_COLLATE=C
        local length="${#1}"
        for (( i = 0; i < length; i++ )); do
                local c="${1:$i:1}"
                case $c in
                        [a-zA-Z0-9.~_-]) printf '%s' "$c" ;;
                        *) printf '%%%02X' "'$c" ;;
                esac
        done
        LC_COLLATE=$old_lc_collate
}

function urldecode() {
        # urldecode <string>
        local url_encoded="${1//+/ }"
        printf '%b' "${url_encoded//%/\\x}"
}

function pusher(){
        token="1318386267:AAHOq8X5lqpjkWfPnXJh3etK8JyDKt1YNCI"
        id="202499999"
        msj=$@
        if [ "$msj" == "" ]; then
                if [ ! -t 0 ]; then
                        msj=$(cat /dev/stdin)
                else
                        msj="beep"
                fi
        fi
        msj=$(urlencode "$msj")
        url="https://api.telegram.org/bot$token/sendMessage"
        curl -s -X POST "$url" -d chat_id="$id" -d text="$msj" &> /dev/null
        if [ $? -ne 0 ]; then
                echo "Error with bot"
        fi
}
Enter fullscreen mode Exit fullscreen mode
source ~/.bashrc
Enter fullscreen mode Exit fullscreen mode

Luego se ejecuta desde la terminal usando el nombre de la función pusher. Tenemos tres formas de ejecutar:

  • STDIN. $ echo test text | pusher
  • Input por argumentos. $ pusher test text
  • Sin argumentos. $ pusher

Image description

Casos de uso

Es importante tener en cuenta para qué usaremos esta herramienta y las limitaciones que tendremos. Acomodé el script para que pueda ser usado como herramienta, independiente del ~/.bashrc, para casos particulares en los cuales no se pueda usar el recurso desde ahí, por ejemplo cuando ejecutamos una tarea programada en el crontab o usamos xargs, parallel o cuando ejecutamos dentro de un script en Bash:

notify.sh

Herramienta que se usa en línea de comandos para notificar por Telegram.

#!/bin/bash

function urlencode() {
        # urlencode <string>
        old_lc_collate=$LC_COLLATE
        LC_COLLATE=C
        local length="${#1}"
        for (( i = 0; i < length; i++ )); do
                local c="${1:$i:1}"
                case $c in
                        [a-zA-Z0-9.~_-]) printf '%s' "$c" ;;
                        *) printf '%%%02X' "'$c" ;;
                esac
        done
        LC_COLLATE=$old_lc_collate
}

function urldecode() {
        # urldecode <string>
        local url_encoded="${1//+/ }"
        printf '%b' "${url_encoded//%/\\x}"
}

token="1318386267:AAHOq8X5lqpjkWfPnXJh3etK8JyDKt1YNCI"
id="202499999"
msj=$@
if [ "$msj" == "" ]; then
        if [ ! -t 0 ]; then
                msj=$(cat /dev/stdin)
        else
                msj="beep"
        fi
fi
msj=$(urlencode "$msj")
url="https://api.telegram.org/bot$token/sendMessage"
curl -s -X POST "$url" -d chat_id="$id" -d text="$msj" &> /dev/null
if [ $? -ne 0 ]; then
        echo "Error with bot"
fi
Enter fullscreen mode Exit fullscreen mode

Luego damos permiso de ejecución al script y lo movemos a /usr/bin/notify.sh:

chmod +x notify.sh
sudo mv notify.sh /usr/bin/notify.sh
Enter fullscreen mode Exit fullscreen mode

Ejemplos

Bug bounty y pentesting para poder identificar si un ataque fue efectivo o no usando un if con los estados de salida de Bash ($?) y enviando la salida del comando al bot. En caso de querer realizar un comando de una línea es posible usar el operador booleano and (&&) y or (||).

Ejemplo para notificar cada dominio existente en cada mensaje.

cat doms.txt | xargs -P7 -I@ "host @ && echo @ | notify.sh"
Enter fullscreen mode Exit fullscreen mode

Ejemplo para notificar todos los dominios existentes en un solo mensaje.

#!/bin/bash

while read line; do
    host $line 
done < doms.txt | pusher
Enter fullscreen mode Exit fullscreen mode
  • Crontab, útil para tener un seguimiento de las tareas programadas que se estén llevando a cabo.

Al ejecutar crontab -e se despliega una pantalla donde uno puede programar tareas para que se ejecuten cada cierto tiempo o a una cierta hora. Un ejemplo para aplicar el notificador es el siguiente:

Ejemplo con tarea en crontab revisando un portal cada una hora.

0 * * * * curl http://evil.corp && echo hola || notify.sh problemas con el portal $(date)
Enter fullscreen mode Exit fullscreen mode
  • Control de logs, en algunas ocasiones es necesario saber qué pasa por nuestro servidor para eso diseñamos un script que envía los resultados al bot en tiempo real.

hotreader.sh

Script que envía notificaciones con las líneas añadidas de un archivo en tiempo real.

#!/bin/bash

file=$1; text=$2
if [ "$text" == "" ]; then text="."; fi

lines=$(cat $file | wc -l)
#while inotifywait -q -e modify $file; do
inotifywait -q -m -e modify $file | while read filename event; do 
    linesNow=$(cat $file | wc -l)
    tail -n $(($linesNow-$lines)) $file | grep $text | notify.sh
    lines=$linesNow
done
Enter fullscreen mode Exit fullscreen mode

Modo de uso:

bash hotreader.sh <TEXTFIlE> <PATTERN>
Enter fullscreen mode Exit fullscreen mode

Leer logs DNS:

Herramienta que notifica las consultas DNS realizadas al servidor.

bash hotreader.sh /var/log/named/query.log custom-domain.org
Enter fullscreen mode Exit fullscreen mode

Conclusión

Esta herramienta que creamos prueba la versatilidad y utilidad de un lenguaje como Bash, que en conjunto con una herramienta como BotFather, puede utilizarse para todo tipo de tareas y en varias áreas. El notificador se puede usar para monitorear todo tipo de tareas, por ejemplo, tales como A, B y C. Lo único que se requiere para expandir los límites de la herramienta es tiempo y creatividad.

Bash le pone.

Bonus track

Telegram Hot reader en Python:

#!/usr/bin/python3

import os.path
import sys
import urllib.request
import urllib.parse

# sudo pip3 install pyinotify
import pyinotify


# Usage
# python3 notifier.py <TextFile>

global lines

file_watcher = os.path.realpath(sys.argv[1])

def count_lines(file_name):
    with open(file_name) as f:
        count = len(f.readlines())
    return count

def tail_n(file_name, n):
    with open(file_name) as f:
        lines = f.readlines()
    return lines[-n:]

def list2string(list):
    return "".join(list)

def sender(msj):
    if msj == "":
        msj = "[HotReader]"

    token = "<TOKEN_BOT>"
    chat_id = "<CHAT_ID>"

    url = f"https://api.telegram.org/bot{token}/sendMessage"

    values = {
        "chat_id": chat_id,
        "text": "[HotReader] " + msj
    }

    data = urllib.parse.urlencode(values)
    data = data.encode('ascii')
    req = urllib.request.Request(url, data)
    urllib.request.urlopen(req)


# Example: monitors transient files.
#
# Run this code, then run transient_file.sh in another shell.


class ProcessTransientFile(pyinotify.ProcessEvent):

    def process_IN_MODIFY(self, event):
        global lines
        # We have explicitely registered for this kind of event.
        #print('\t', event.pathname, ' -> written')
        lines_now = count_lines(file_watcher)
        modified = tail_n(file_watcher, lines_now - lines)
        print(list2string(modified))
        lines = lines_now
        sender(list2string(modified))


    def process_default(self, event):
        # Implicitely IN_CREATE and IN_DELETE are watched too. You can
        # ignore them and provide an empty process_default or you can
        # process them, either with process_default or their dedicated
        # method (process_IN_CREATE, process_IN_DELETE) which would
        # override process_default.
        print('default: ', event.maskname)


lines = count_lines(file_watcher)

wm = pyinotify.WatchManager()
notifier = pyinotify.Notifier(wm)
# In this case you must give the class object (ProcessTransientFile)
# as last parameter not a class instance.
wm.watch_transient_file(file_watcher, pyinotify.IN_MODIFY, ProcessTransientFile)
notifier.loop()
Enter fullscreen mode Exit fullscreen mode

Top comments (0)