Cragen niwclear dros ICMP

Cragen niwclear dros ICMP

TL; DR: Rwy'n ysgrifennu modiwl cnewyllyn a fydd yn darllen gorchmynion o lwyth cyflog ICMP a'u gweithredu ar y gweinydd hyd yn oed os bydd eich SSH yn chwalu. I'r rhai mwyaf diamynedd, mae'r holl god GitHub.

Rhybudd Mae rhaglenwyr C profiadol mewn perygl o dorri i mewn i ddagrau gwaed! Efallai fy mod hyd yn oed yn anghywir yn y derminoleg, ond croesewir unrhyw feirniadaeth. Mae'r swydd wedi'i bwriadu ar gyfer y rhai sydd â syniad bras iawn o raglennu C ac sydd am edrych i mewn i Linux.

Yn y sylwadau i'm cyntaf Erthygl crybwyllodd SoftEther VPN, a all ddynwared rhai protocolau “rheolaidd”, yn enwedig HTTPS, ICMP a hyd yn oed DNS. Gallaf ddychmygu dim ond y cyntaf ohonynt yn gweithio, gan fy mod yn gyfarwydd iawn â HTTP(S), a bu'n rhaid i mi ddysgu twnelu dros ICMP a DNS.

Cragen niwclear dros ICMP

Ydw, yn 2020 dysgais y gallwch chi fewnosod llwyth tâl mympwyol ym mhecynnau ICMP. Ond gwell hwyr na byth! A chan y gellir gwneud rhywbeth yn ei gylch, yna mae angen ei wneud. Gan fy mod yn defnyddio'r llinell orchymyn yn aml yn fy mywyd bob dydd, gan gynnwys trwy SSH, daeth y syniad o gragen ICMP i'm meddwl yn gyntaf. Ac er mwyn llunio bingo tarw cyfan, penderfynais ei ysgrifennu fel modiwl Linux mewn iaith nad oes gennyf ond syniad bras ohoni. Ni fydd cragen o'r fath yn weladwy yn y rhestr o brosesau, gallwch ei lwytho i mewn i'r cnewyllyn ac ni fydd ar y system ffeiliau, ni welwch unrhyw beth amheus yn y rhestr o borthladdoedd gwrando. O ran ei alluoedd, mae hwn yn rootkit llawn, ond rwy'n gobeithio ei wella a'i ddefnyddio fel cragen o ddewis olaf pan fydd y Cyfartaledd Llwyth yn rhy uchel i fewngofnodi trwy SSH a gweithredu o leiaf echo i > /proc/sysrq-triggeri adfer mynediad heb ailgychwyn.

Rydym yn cymryd golygydd testun, sgiliau rhaglennu sylfaenol yn Python a C, Google a rhith nad oes ots gennych ei rhoi o dan y gyllell os bydd popeth yn torri (dewisol - VirtualBox/KVM/etc lleol) a gadewch i ni fynd!

Ochr y cleient

Roedd yn ymddangos i mi y byddai'n rhaid i mi ysgrifennu sgript gyda thua 80 o linellau ar gyfer y rhan cleient, ond roedd yna bobl garedig a wnaeth hynny i mi yr holl waith. Trodd y cod allan i fod yn annisgwyl o syml, yn ffitio i 10 llinell arwyddocaol:

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()

Mae'r sgript yn cymryd dwy ddadl, cyfeiriad a llwyth tâl. Cyn ei anfon, mae allwedd yn rhagflaenu'r llwyth tâl run:, bydd ei angen arnom i eithrio pecynnau gyda llwythi tâl ar hap.

Mae'r cnewyllyn angen breintiau i grefftio pecynnau, felly bydd yn rhaid rhedeg y sgript fel superuser. Peidiwch ag anghofio rhoi caniatâd gweithredu a gosod scapy ei hun. Mae gan Debian becyn o'r enw python3-scapy. Nawr gallwch chi wirio sut mae'r cyfan yn gweithio.

Rhedeg ac allbynnu'r gorchymyn
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!

Dyma sut mae'n edrych yn y 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

Nid yw'r llwyth tâl yn y pecyn ymateb yn newid.

Modiwl cnewyllyn

I gynnwys peiriant rhithwir Debian bydd ei angen arnoch o leiaf make и linux-headers-amd64, bydd y gweddill yn dod ar ffurf dibyniaethau. Ni fyddaf yn darparu'r cod cyfan yn yr erthygl; gallwch ei glonio ar Github.

Gosodiad bachyn

I ddechrau, mae angen dwy swyddogaeth arnom er mwyn llwytho'r modiwl a'i ddadlwytho. Nid oes angen y swyddogaeth ar gyfer dadlwytho, ond wedyn rmmod ni fydd yn gweithio; dim ond pan fydd wedi'i ddiffodd y bydd y modiwl yn cael ei ddadlwytho.

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

Beth sy'n digwydd yma:

  1. Mae dwy ffeil pennawd yn cael eu tynnu i mewn i drin y modiwl ei hun a'r hidlydd rhwyd.
  2. Mae pob gweithrediad yn mynd trwy hidlydd rhwyd, gallwch chi osod bachau ynddo. I wneud hyn, mae angen i chi ddatgan y strwythur y bydd y bachyn yn cael ei ffurfweddu ynddo. Y peth pwysicaf yw nodi'r swyddogaeth a fydd yn cael ei chyflawni fel bachyn: nfho.hook = icmp_cmd_executor; Byddaf yn cyrraedd y swyddogaeth ei hun yn ddiweddarach.
    Yna gosodais yr amser prosesu ar gyfer y pecyn: NF_INET_PRE_ROUTING yn pennu i brosesu'r pecyn pan fydd yn ymddangos gyntaf yn y cnewyllyn. Gellir ei ddefnyddio NF_INET_POST_ROUTING i brosesu'r pecyn wrth iddo adael y cnewyllyn.
    Gosodais yr hidlydd i IPv4: nfho.pf = PF_INET;.
    Rwy'n rhoi'r flaenoriaeth uchaf i'm bachyn: nfho.priority = NF_IP_PRI_FIRST;
    Ac rwy'n cofrestru'r strwythur data fel y bachyn gwirioneddol: nf_register_net_hook(&init_net, &nfho);
  3. Mae'r swyddogaeth derfynol yn tynnu'r bachyn.
  4. Mae'r drwydded wedi'i nodi'n glir fel nad yw'r casglwr yn cwyno.
  5. Swyddogaethau module_init() и module_exit() gosod swyddogaethau eraill i gychwyn a therfynu'r modiwl.

Adalw'r llwyth tâl

Nawr mae angen i ni dynnu'r llwyth tâl, dyma'r dasg anoddaf. Nid oes gan y cnewyllyn swyddogaethau adeiledig ar gyfer gweithio gyda llwythi tâl; dim ond penawdau protocolau lefel uwch y gallwch chi eu dosrannu.

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

Beth sy'n Digwydd:

  1. Roedd yn rhaid i mi gynnwys ffeiliau pennawd ychwanegol, y tro hwn i drin penawdau IP ac ICMP.
  2. Gosodais uchafswm hyd y llinell: #define MAX_CMD_LEN 1976. Pam yn union hyn? Oherwydd bod y casglwr yn cwyno amdano! Maen nhw eisoes wedi awgrymu i mi fod angen i mi ddeall y pentwr a'r domen, ryw ddydd byddaf yn bendant yn gwneud hyn ac efallai hyd yn oed yn cywiro'r cod. Gosodais y llinell ar unwaith a fydd yn cynnwys y gorchymyn: char cmd_string[MAX_CMD_LEN];. Dylai fod yn weladwy ym mhob swyddogaeth; byddaf yn siarad am hyn yn fanylach ym mharagraff 9.
  3. Nawr mae angen i ni gychwyn (struct work_struct my_work;) strwythuro a'i gysylltu â swyddogaeth arall (DECLARE_WORK(my_work, work_handler);). Soniaf hefyd pam y mae hyn yn angenrheidiol yn y nawfed paragraff.
  4. Nawr rwy'n datgan swyddogaeth, a fydd yn fachyn. Mae'r math a'r dadleuon a dderbynnir yn cael eu pennu gan y hidlydd net, dim ond diddordeb sydd gennym ni skb. Clustog soced yw hwn, strwythur data sylfaenol sy'n cynnwys yr holl wybodaeth sydd ar gael am becyn.
  5. Er mwyn i'r swyddogaeth weithio, bydd angen dau strwythur a sawl newidyn arnoch, gan gynnwys dau iterator.
      struct iphdr *iph;
      struct icmphdr *icmph;
    
      unsigned char *user_data;
      unsigned char *tail;
      unsigned char *i;
      int j = 0;
  6. Gallwn ddechrau gyda rhesymeg. Er mwyn i'r modiwl weithio, nid oes angen unrhyw becynnau heblaw ICMP Echo, felly rydyn ni'n dosrannu'r byffer gan ddefnyddio swyddogaethau adeiledig ac yn taflu pob pecyn nad yw'n ICMP a heb fod yn Echo allan. Dychwelyd NF_ACCEPT yn golygu derbyn y pecyn, ond gallwch hefyd ollwng pecynnau trwy ddychwelyd 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;
      }

    Nid wyf wedi profi beth fydd yn digwydd heb wirio'r penawdau IP. Mae fy ngwybodaeth leiaf am C yn dweud wrthyf fod rhywbeth ofnadwy yn siŵr o ddigwydd heb wiriadau ychwanegol. Byddaf yn falch os byddwch yn fy narbwyllo o hyn!

  7. Nawr bod y pecyn o'r union fath sydd ei angen arnoch chi, gallwch chi echdynnu'r data. Heb swyddogaeth adeiledig, yn gyntaf mae'n rhaid i chi gael pwyntydd i ddechrau'r llwyth tâl. Gwneir hyn mewn un lle, mae angen i chi fynd â'r pwyntydd i ddechrau pennawd yr ICMP a'i symud i faint y pennawd hwn. Mae popeth yn defnyddio strwythur icmph: user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
    Rhaid i ddiwedd y pennawd gyd-fynd â diwedd y llwyth tâl i mewn skb, felly rydym yn ei gael gan ddefnyddio dulliau niwclear o'r strwythur cyfatebol: tail = skb_tail_pointer(skb);.

    Cragen niwclear dros ICMP

    Cafodd y llun ei ddwyn felly, gallwch ddarllen mwy am y byffer soced.

  8. Unwaith y bydd gennych awgrymiadau i'r dechrau a'r diwedd, gallwch gopïo'r data i linyn cmd_string, gwiriwch ef am bresenoldeb rhagddodiad run: a, naill ai taflu'r pecyn os yw ar goll, neu ailysgrifennu'r llinell eto, gan ddileu'r rhagddodiad hwn.
  9. Dyna ni, nawr gallwch chi ffonio triniwr arall: schedule_work(&my_work);. Gan na fydd yn bosibl trosglwyddo paramedr i alwad o'r fath, rhaid i'r llinell gyda'r gorchymyn fod yn un fyd-eang. schedule_work() yn gosod y swyddogaeth sy'n gysylltiedig â'r strwythur a basiwyd yn y ciw cyffredinol o'r trefnydd tasgau ac yn ei chwblhau, gan ganiatáu i chi beidio ag aros i'r gorchymyn gwblhau. Mae hyn yn angenrheidiol oherwydd mae'n rhaid i'r bachyn fod yn gyflym iawn. Fel arall, eich dewis yw na fydd unrhyw beth yn dechrau neu fe gewch banig cnewyllyn. Mae oedi fel marwolaeth!
  10. Dyna ni, gallwch dderbyn y pecyn gyda dychweliad cyfatebol.

Yn galw rhaglen yn y gofod defnyddiwr

Y swyddogaeth hon yw'r mwyaf dealladwy. Rhoddwyd ei enw yn DECLARE_WORK(), nid yw'r math a'r dadleuon a dderbynnir yn ddiddorol. Rydyn ni'n cymryd y llinell gyda'r gorchymyn ac yn ei drosglwyddo'n gyfan gwbl i'r gragen. Gadewch iddo ddelio â dosrannu, chwilio am deuaidd a phopeth arall.

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. Gosodwch y dadleuon i amrywiaeth o linynnau argv[]. Byddaf yn tybio bod pawb yn gwybod bod rhaglenni'n cael eu gweithredu fel hyn mewn gwirionedd, ac nid fel llinell barhaus gyda bylchau.
  2. Gosod newidynnau amgylchedd. Dim ond PATH a fewnosodais gyda set leiaf o lwybrau, gan obeithio eu bod i gyd eisoes wedi'u cyfuno /bin с /usr/bin и /sbin с /usr/sbin. Anaml iawn y mae llwybrau eraill o bwys yn ymarferol.
  3. Wedi'i wneud, gadewch i ni ei wneud! Swyddogaeth cnewyllyn call_usermodehelper() yn derbyn mynediad. llwybr i'r deuaidd, amrywiaeth o ddadleuon, amrywiaeth o newidynnau amgylchedd. Yma rwyf hefyd yn tybio bod pawb yn deall ystyr pasio'r llwybr i'r ffeil gweithredadwy fel dadl ar wahân, ond gallwch ofyn. Mae'r ddadl olaf yn nodi a ddylid aros i'r broses gwblhau (UMH_WAIT_PROC), dechrau'r broses (UMH_WAIT_EXEC) neu beidio ag aros o gwbl (UMH_NO_WAIT). A oes mwy UMH_KILLABLE, Wnes i ddim edrych i mewn iddo.

Cynulliad

Perfformir cydosod modiwlau cnewyllyn trwy'r fframwaith gwneud cnewyllyn. Wedi galw make y tu mewn i gyfeiriadur arbennig sy'n gysylltiedig â'r fersiwn cnewyllyn (a ddiffinnir yma: KERNELDIR:=/lib/modules/$(shell uname -r)/build), ac mae lleoliad y modiwl yn cael ei drosglwyddo i'r newidyn M yn y dadleuon. Mae'r icmpshell.ko a thargedau glân yn defnyddio'r fframwaith hwn yn gyfan gwbl. YN obj-m yn nodi'r ffeil gwrthrych a fydd yn cael ei drawsnewid yn fodiwl. Cystrawen sy'n ail-wneud main.o в icmpshell.o (icmpshell-objs = main.o) ddim yn edrych yn rhesymegol iawn i mi, ond bydded felly.

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

Rydym yn casglu: make. Wrthi'n llwytho: insmod icmpshell.ko. Wedi'i wneud, gallwch wirio: sudo ./send.py 45.11.26.232 "date > /tmp/test". Os oes gennych ffeil ar eich peiriant /tmp/test ac mae'n cynnwys y dyddiad yr anfonwyd y cais, sy'n golygu eich bod wedi gwneud popeth yn iawn a gwnes i bopeth yn iawn.

Casgliad

Roedd fy mhrofiad cyntaf gyda datblygu niwclear yn llawer haws nag yr oeddwn yn ei ddisgwyl. Hyd yn oed heb brofiad yn datblygu yn C, gan ganolbwyntio ar awgrymiadau casglwr a chanlyniadau Google, roeddwn yn gallu ysgrifennu modiwl gwaith a theimlo fel haciwr cnewyllyn, ac ar yr un pryd yn kiddie sgript. Yn ogystal, es i sianel Kernel Newbies, lle dywedwyd wrthyf i ddefnyddio schedule_work() yn lle galw call_usermodehelper() y tu mewn i'r bachyn ei hun a'i gywilyddio, gan amau ​​​​twyll. Costiodd can llinell o god tua wythnos o ddatblygiad i mi yn fy amser rhydd. Profiad llwyddiannus a ddinistriodd fy myth personol am gymhlethdod llethol datblygiad systemau.

Os bydd rhywun yn cytuno i wneud adolygiad cod ar Github, byddaf yn ddiolchgar. Dwi'n eitha siwr mod i wedi gwneud lot o gamgymeriadau dwp, yn enwedig wrth weithio efo llinynnau.

Cragen niwclear dros ICMP

Ffynhonnell: hab.com

Ychwanegu sylw