¿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.
- En el bot se pueden usar las siguientes opciones:
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.
Programando el bot
Ya creado el bot, accedemos a su chat desde el link entregado luego de configurarlo y damos a /start
.
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.
A continuación debemos obtener id del chat, con el siguiente link:
https://api.telegram.org/bot<YourBOTToken>/getUpdates
Este es un ejemplo de cómo obtener el dato necesario usando curl:
curl https://api.telegram.org/bot1318386267:AAHOq8X5lqpjkWfPnXJh3etK8JyDKt1YNCI/getUpdates -s | jq
El campo id es nuestro identificador de chat, que será necesario para que nos lleguen las notificaciones a nuestra cuenta.
- 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
}
Para poder hacer válidos los cambios en el ~/.bashrc
es necesario usar el comando source
.
source ~/.bashrc
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
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
#!/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
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
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"
Ejemplo para notificar todos los dominios existentes en un solo mensaje:
#!/bin/bash
while read line; do
host $line
done < doms.txt | pusher
Ejemplo para notificar cuando se encuentren credenciales de autenticación básica:
#!/bin/bash
while read line; do
curl -i -u "admin:$line" http://evil.corp/basicauth | head -1 | grep 200
if [ $? -eq 0 ]; then
echo "creds found: admin:$line" | pusher
break
fi
done < passwds.txt
- 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)
- 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
Modo de uso:
bash hotreader.sh <TEXTFIlE> <PATTERN>
Leer logs DNS:
bash hotreader.sh /var/log/named/query.log custom-domain.org
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()
Thanks Daniel Barrientos & elborikua.