mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-07 05:05:12 +02:00
Merge branch 'SYSRQ'
This commit is contained in:
@@ -1,17 +1,19 @@
|
||||
The SYSRQ target allows to remotely trigger sysrq on the local machine over the
|
||||
network. This can be useful when vital parts of the machine hang, for example
|
||||
an oops in a filesystem causing locks to be not released and processes to get
|
||||
stuck as a result -- if still possible, use /proc/sysrq-trigger. Even when
|
||||
stuck as a result - if still possible, use /proc/sysrq-trigger. Even when
|
||||
processes are stuck, interrupts are likely to be still processed, and as such,
|
||||
sysrq can be triggered through incoming network packets.
|
||||
.PP
|
||||
This xt_SYSRQ implementation does not use any encryption, so you should change
|
||||
the SYSRQ password after use unless you have made sure it was transmitted
|
||||
securely and no one sniffed the network, e.g. by use of an IPsec tunnel whose
|
||||
endpoint is at the machine where you want to trigger the sysrq. Also, you
|
||||
should limit as to who can issue commands using \fB-s\fP and/or \fB-m mac\fP,
|
||||
and also that the destination is correct using \fB-d\fP (to protect against
|
||||
potential broadcast packets), noting that it is still short of MAC/IP spoofing:
|
||||
The xt_SYSRQ implementation uses a salted hash and a sequence number to prevent
|
||||
network sniffers from either guessing the password or replaying earlier
|
||||
requests. The initial sequence number comes from the time of day so you will
|
||||
have a small window of vulnerability should time go backwards at a reboot.
|
||||
However, the file /sys/module/xt_SYSREQ/seqno can be used to both query and
|
||||
update the current sequence number. Also, you should limit as to who can issue
|
||||
commands using \fB-s\fP and/or \fB-m mac\fP, and also that the destination is
|
||||
correct using \fB-d\fP (to protect against potential broadcast packets), noting
|
||||
that it is still short of MAC/IP spoofing:
|
||||
.IP
|
||||
-A INPUT -s 10.10.25.1 -m mac --mac-source aa:bb:cc:dd:ee:ff -d 10.10.25.7
|
||||
-p udp --dport 9 -j SYSRQ
|
||||
@@ -20,28 +22,59 @@ potential broadcast packets), noting that it is still short of MAC/IP spoofing:
|
||||
ipsec --proto esp --tunnel-src 10.10.25.1 --tunnel-dst 10.10.25.7
|
||||
-p udp --dport 9 -j SYSRQ
|
||||
.PP
|
||||
You should also limit the rate at which connections can be received to limit
|
||||
the CPU time taken by illegal requests, for example:
|
||||
.IP
|
||||
-A INPUT 0s 10.10.25.1 -m mac --mac-source aa:bb:cc:dd:ee:ff -d 10.10.25.7
|
||||
-p udp --dport 9 -m limit --limit 5/minute -j SYSRQ
|
||||
.PP
|
||||
This extension does not take any options. The \fB-p udp\fP options are
|
||||
required.
|
||||
.PP
|
||||
The SYSRQ password can be changed through
|
||||
/sys/module/xt_SYSRQ/parameters/password; note you need to use `echo -n` to
|
||||
not add a newline to the password, i.e.
|
||||
/sys/module/xt_SYSRQ/parameters/password, for example:
|
||||
.IP
|
||||
echo -n "password" >/sys/.../password
|
||||
echo -n "password" >/sys/module/xt_SYSRQ/parameters/password
|
||||
.PP
|
||||
Alternatively, the password may be specified at modprobe time, but this is
|
||||
insecure as people can possible see it through ps(1). You can use an option
|
||||
line in /etc/modprobe.d/sysrq if it is properly guarded, that is, only readable
|
||||
by root.
|
||||
line in e.g. /etc/modprobe.d/xt_sysrq if it is properly guarded, that is, only
|
||||
readable by root.
|
||||
.IP
|
||||
options xt_SYSRQ password=cookies
|
||||
.PP
|
||||
To trigger SYSRQ from a remote host, just use netcat or socat, specifying the
|
||||
action (only one) as first character, followed by the password:
|
||||
The hash algorithm can also be specified as a module option, for example, to
|
||||
use SHA-256 instead of the default SHA-1:
|
||||
.IP
|
||||
echo -n "scookies" | socat stdin udp-sendto:10.10.25.7:9
|
||||
.IP
|
||||
echo -n "scookies" | netcat -u 10.10.25.7 9
|
||||
options xt_SYSRQ hash=sha256
|
||||
.PP
|
||||
See the Linux docs for possible sysrq keys. Important ones are:
|
||||
re(b)oot, power(o)ff, (s)ync filesystems, (u)mount and remount readonly.
|
||||
The xt_SYSRQ module is normally silent unless a successful request is received,
|
||||
but the \fIdebug\fP module parameter can be used to find exactly why a
|
||||
seemingly correct request is not being processed.
|
||||
.PP
|
||||
To trigger SYSRQ from a remote host, just use netcat or socat:
|
||||
.PP
|
||||
.nf
|
||||
sysrq_key="s" # the SysRq key(s)
|
||||
password="password"
|
||||
seqno="$(date +%s)"
|
||||
salt="$(dd bs=12 count=1 if=/dev/urandom 2>/dev/null |
|
||||
openssl enc -base64)"
|
||||
req="$sysrq_key,$seqno,$salt"
|
||||
req="$req,$(echo -n "$req,$password" | sha1sum | cut -c1-40)"
|
||||
|
||||
echo "$req" | socat stdin udp-sendto:10.10.25.7:9
|
||||
# or
|
||||
echo "$req" | netcat -uw1 10.10.25.7 9
|
||||
.fi
|
||||
.PP
|
||||
See the Linux docs for possible sysrq keys. Important ones are: re(b)oot,
|
||||
power(o)ff, (s)ync filesystems, (u)mount and remount readonly. More than one
|
||||
sysrq key can be used at once, but bear in mind that, for example, a sync may
|
||||
not complete before a subsequent reboot or poweroff.
|
||||
.PP
|
||||
The hashing scheme should be enough to prevent mis-use of SYSRQ in many
|
||||
environments, but it is not perfect: take reasonable precautions to
|
||||
protect your machines. Most importantly ensure that each machine has a
|
||||
different password; there is scant protection for a SYSRQ packet being
|
||||
applied to a machine that happens to have the same password.
|
||||
|
@@ -3,7 +3,6 @@
|
||||
* Copyright © Jan Engelhardt <jengelh [at] medozas de>, 2008
|
||||
*
|
||||
* Based upon the ipt_SYSRQ idea by Marek Zalem <marek [at] terminus sk>
|
||||
* xt_SYSRQ does not use hashing or timestamps.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -19,15 +18,145 @@
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <net/ip.h>
|
||||
#include "compat_xtables.h"
|
||||
|
||||
static bool sysrq_once;
|
||||
static char sysrq_password[64];
|
||||
static char sysrq_hash[16] = "sha1";
|
||||
static long sysrq_seqno;
|
||||
static int sysrq_debug;
|
||||
module_param_string(password, sysrq_password, sizeof(sysrq_password),
|
||||
S_IRUSR | S_IWUSR);
|
||||
module_param_string(hash, sysrq_hash, sizeof(sysrq_hash), S_IRUSR);
|
||||
module_param_named(seqno, sysrq_seqno, long, S_IRUSR | S_IWUSR);
|
||||
module_param_named(debug, sysrq_debug, int, S_IRUSR | S_IWUSR);
|
||||
MODULE_PARM_DESC(password, "password for remote sysrq");
|
||||
MODULE_PARM_DESC(hash, "hash algorithm, default sha1");
|
||||
MODULE_PARM_DESC(seqno, "sequence number for remote sysrq");
|
||||
MODULE_PARM_DESC(debug, "debugging: 0=off, 1=on");
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
|
||||
static struct crypto_hash *sysrq_tfm;
|
||||
static int sysrq_digest_size;
|
||||
static unsigned char *sysrq_digest_password;
|
||||
static unsigned char *sysrq_digest;
|
||||
static char *sysrq_hexdigest;
|
||||
|
||||
/*
|
||||
* The data is of the form "<requests>,<seqno>,<salt>,<hash>" where <requests>
|
||||
* is a series of sysrq requests; <seqno> is a sequence number that must be
|
||||
* greater than the last sequence number; <salt> is some random bytes; and
|
||||
* <hash> is the hash of everything up to and including the preceding ","
|
||||
* together with the password.
|
||||
*
|
||||
* For example
|
||||
*
|
||||
* salt=$RANDOM
|
||||
* req="s,$(date +%s),$salt"
|
||||
* echo "$req,$(echo -n $req,secret | sha1sum | cut -c1-40)"
|
||||
*
|
||||
* You will want a better salt and password than that though :-)
|
||||
*/
|
||||
static unsigned int sysrq_tg(const void *pdata, uint16_t len)
|
||||
{
|
||||
const char *data = pdata;
|
||||
int i, n;
|
||||
struct scatterlist sg[2];
|
||||
struct hash_desc desc;
|
||||
int ret;
|
||||
long new_seqno = 0;
|
||||
|
||||
if (*sysrq_password == '\0') {
|
||||
if (!sysrq_once)
|
||||
printk(KERN_INFO KBUILD_MODNAME ": No password set\n");
|
||||
sysrq_once = true;
|
||||
return NF_DROP;
|
||||
}
|
||||
if (len == 0)
|
||||
return NF_DROP;
|
||||
|
||||
for (i = 0; sysrq_password[i] != '\0' &&
|
||||
sysrq_password[i] != '\n'; ++i)
|
||||
/* loop */;
|
||||
sysrq_password[i] = '\0';
|
||||
|
||||
i = 0;
|
||||
for (n = 0; n < len - 1; ++n) {
|
||||
if (i == 1 && '0' <= data[n] && data[n] <= '9')
|
||||
new_seqno = 10L * new_seqno + data[n] - '0';
|
||||
if (data[n] == ',' && ++i == 3)
|
||||
break;
|
||||
}
|
||||
++n;
|
||||
if (i != 3) {
|
||||
if (sysrq_debug)
|
||||
printk(KERN_WARNING KBUILD_MODNAME
|
||||
": badly formatted request\n");
|
||||
return NF_DROP;
|
||||
}
|
||||
if (sysrq_seqno >= new_seqno) {
|
||||
if (sysrq_debug)
|
||||
printk(KERN_WARNING KBUILD_MODNAME
|
||||
": old sequence number ignored\n");
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
desc.tfm = sysrq_tfm;
|
||||
desc.flags = 0;
|
||||
ret = crypto_hash_init(&desc);
|
||||
if (ret != 0)
|
||||
goto hash_fail;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
|
||||
sg_init_table(sg, 2);
|
||||
#endif
|
||||
sg_set_buf(&sg[0], data, n);
|
||||
strcpy(sysrq_digest_password, sysrq_password);
|
||||
i = strlen(sysrq_digest_password);
|
||||
sg_set_buf(&sg[1], sysrq_digest_password, i);
|
||||
ret = crypto_hash_digest(&desc, sg, n + i, sysrq_digest);
|
||||
if (ret != 0)
|
||||
goto hash_fail;
|
||||
|
||||
for (i = 0; i < sysrq_digest_size; ++i) {
|
||||
sysrq_hexdigest[2*i] =
|
||||
"0123456789abcdef"[(sysrq_digest[i] >> 4) & 0xf];
|
||||
sysrq_hexdigest[2*i+1] =
|
||||
"0123456789abcdef"[sysrq_digest[i] & 0xf];
|
||||
}
|
||||
sysrq_hexdigest[2*sysrq_digest_size] = '\0';
|
||||
if (len - n < sysrq_digest_size) {
|
||||
if (sysrq_debug)
|
||||
printk(KERN_INFO KBUILD_MODNAME ": Short digest,"
|
||||
" expected %s\n", sysrq_hexdigest);
|
||||
return NF_DROP;
|
||||
}
|
||||
if (strncmp(data + n, sysrq_hexdigest, sysrq_digest_size) != 0) {
|
||||
if (sysrq_debug)
|
||||
printk(KERN_INFO KBUILD_MODNAME ": Bad digest,"
|
||||
" expected %s\n", sysrq_hexdigest);
|
||||
return NF_DROP;
|
||||
}
|
||||
|
||||
/* Now we trust the requester */
|
||||
sysrq_seqno = new_seqno;
|
||||
for (i = 0; i < len && data[i] != ','; ++i) {
|
||||
printk(KERN_INFO KBUILD_MODNAME ": SysRq %c\n", data[i]);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
|
||||
handle_sysrq(data[i], NULL);
|
||||
#else
|
||||
handle_sysrq(data[i], NULL, NULL);
|
||||
#endif
|
||||
}
|
||||
return NF_ACCEPT;
|
||||
|
||||
hash_fail:
|
||||
printk(KERN_WARNING KBUILD_MODNAME ": digest failure\n");
|
||||
return NF_DROP;
|
||||
}
|
||||
#else
|
||||
static unsigned int sysrq_tg(const void *pdata, uint16_t len)
|
||||
{
|
||||
const char *data = pdata;
|
||||
@@ -57,6 +186,7 @@ static unsigned int sysrq_tg(const void *pdata, uint16_t len)
|
||||
#endif
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned int
|
||||
sysrq_tg4(struct sk_buff **pskb, const struct xt_target_param *par)
|
||||
@@ -73,9 +203,11 @@ sysrq_tg4(struct sk_buff **pskb, const struct xt_target_param *par)
|
||||
udph = (void *)iph + ip_hdrlen(skb);
|
||||
len = ntohs(udph->len) - sizeof(struct udphdr);
|
||||
|
||||
printk(KERN_INFO KBUILD_MODNAME ": " NIPQUAD_FMT ":%u -> :%u len=%u\n",
|
||||
NIPQUAD(iph->saddr), htons(udph->source), htons(udph->dest),
|
||||
len);
|
||||
if (sysrq_debug)
|
||||
printk(KERN_INFO KBUILD_MODNAME
|
||||
": " NIPQUAD_FMT ":%u -> :%u len=%u\n",
|
||||
NIPQUAD(iph->saddr), htons(udph->source),
|
||||
htons(udph->dest), len);
|
||||
return sysrq_tg((void *)udph + sizeof(struct udphdr), len);
|
||||
}
|
||||
|
||||
@@ -94,14 +226,17 @@ sysrq_tg6(struct sk_buff **pskb, const struct xt_target_param *par)
|
||||
udph = udp_hdr(skb);
|
||||
len = ntohs(udph->len) - sizeof(struct udphdr);
|
||||
|
||||
printk(KERN_INFO KBUILD_MODNAME ": " NIP6_FMT ":%hu -> :%hu len=%u\n",
|
||||
NIP6(iph->saddr), ntohs(udph->source),
|
||||
ntohs(udph->dest), len);
|
||||
if (sysrq_debug)
|
||||
printk(KERN_INFO KBUILD_MODNAME
|
||||
": " NIP6_FMT ":%hu -> :%hu len=%u\n",
|
||||
NIP6(iph->saddr), ntohs(udph->source),
|
||||
ntohs(udph->dest), len);
|
||||
return sysrq_tg(udph + sizeof(struct udphdr), len);
|
||||
}
|
||||
|
||||
static bool sysrq_tg_check(const struct xt_tgchk_param *par)
|
||||
{
|
||||
|
||||
if (par->target->family == NFPROTO_IPV4) {
|
||||
const struct ipt_entry *entry = par->entryinfo;
|
||||
|
||||
@@ -146,11 +281,64 @@ static struct xt_target sysrq_tg_reg[] __read_mostly = {
|
||||
|
||||
static int __init sysrq_tg_init(void)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
|
||||
struct timeval now;
|
||||
|
||||
sysrq_tfm = crypto_alloc_hash(sysrq_hash, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(sysrq_tfm)) {
|
||||
printk(KERN_WARNING KBUILD_MODNAME
|
||||
": Error: Could not find or load %s hash\n",
|
||||
sysrq_hash);
|
||||
sysrq_tfm = NULL;
|
||||
goto fail;
|
||||
}
|
||||
sysrq_digest_size = crypto_hash_digestsize(sysrq_tfm);
|
||||
sysrq_digest = kmalloc(sysrq_digest_size, GFP_KERNEL);
|
||||
if (sysrq_digest == NULL) {
|
||||
printk(KERN_WARNING KBUILD_MODNAME
|
||||
": Cannot allocate digest\n");
|
||||
goto fail;
|
||||
}
|
||||
sysrq_hexdigest = kmalloc(2 * sysrq_digest_size + 1, GFP_KERNEL);
|
||||
if (sysrq_hexdigest == NULL) {
|
||||
printk(KERN_WARNING KBUILD_MODNAME
|
||||
": Cannot allocate hexdigest\n");
|
||||
goto fail;
|
||||
}
|
||||
sysrq_digest_password = kmalloc(sizeof(sysrq_password), GFP_KERNEL);
|
||||
if (sysrq_digest_password == NULL) {
|
||||
printk(KERN_WARNING KBUILD_MODNAME
|
||||
": Cannot allocate password digest space\n");
|
||||
goto fail;
|
||||
}
|
||||
do_gettimeofday(&now);
|
||||
sysrq_seqno = now.tv_sec;
|
||||
return xt_register_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg));
|
||||
|
||||
fail:
|
||||
if (sysrq_tfm)
|
||||
crypto_free_hash(sysrq_tfm);
|
||||
if (sysrq_digest)
|
||||
kfree(sysrq_digest);
|
||||
if (sysrq_hexdigest)
|
||||
kfree(sysrq_hexdigest);
|
||||
if (sysrq_digest_password)
|
||||
kfree(sysrq_digest_password);
|
||||
return -EINVAL;
|
||||
#else
|
||||
printk(KERN_WARNING "xt_SYSRQ does not provide crypto for <= 2.6.18\n");
|
||||
return xt_register_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __exit sysrq_tg_exit(void)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
|
||||
crypto_free_hash(sysrq_tfm);
|
||||
kfree(sysrq_digest);
|
||||
kfree(sysrq_hexdigest);
|
||||
kfree(sysrq_digest_password);
|
||||
#endif
|
||||
return xt_unregister_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user