Here are 6 Linux kernel local infoleaks.
InfoSect is available for engagements in code review. Please look at http://infosectcbr.com.au/consulting or check out some of our public code review videos on http://youtube.com/c/InfoSect. Or, check out our code review training at http://infosectcbr.com.au/training.
1 ==========================
/usr/src/linux-source-4.14/drivers/net/appletalk/ipddp.h
struct ipddp_route
{
struct net_device *dev; /* Carrier device */
__be32 ip; /* IP address */
struct atalk_addr at; /* Gateway appletalk address */
int flags;
struct ipddp_route *next;
};
/usr/src/linux-source-4.14/drivers/net/appletalk/ipddp.c
static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt)
{
struct ipddp_route *f;
for(f = ipddp_route_list; f != NULL; f = f->next)
{
if(f->ip == rt->ip &&
f->at.s_net == rt->at.s_net &&
f->at.s_node == rt->at.s_node)
return f;
}
return NULL;
}
...
case SIOCFINDIPDDPRT:
spin_lock_bh(&ipddp_route_lock);
rp = __ipddp_find_route(&rcp);
if (rp)
memcpy(&rcp2, rp, sizeof(rcp2));
spin_unlock_bh(&ipddp_route_lock);
if (rp) {
if (copy_to_user(rt, &rcp2,
sizeof(struct ipddp_route)))
return -EFAULT;
return 0;
} else
return -ENOENT;
++ in the ipddp_route struct, there are pointers next and dev which get
++ leaked here.
2 ===================================================
struct snd_ctl_elem_list {
unsigned int offset; /* W: first element ID to get */
unsigned int space; /* W: count of element IDs to get */
unsigned int used; /* R: count of element IDs set */
unsigned int count; /* R: count of all elements */
struct snd_ctl_elem_id __user *pids; /* R: IDs */
unsigned char reserved[50];
};
+++ note reserved, pids
/usr/src/linux-source-4.14/sound/core/control.c
static int snd_ctl_elem_list(struct snd_card *card,
struct snd_ctl_elem_list __user *_list)
{
struct snd_ctl_elem_list list;
struct snd_kcontrol *kctl;
struct snd_ctl_elem_id id;
unsigned int offset, space, jidx;
int err = 0;
...
list_for_each_entry(kctl, &card->controls, list) {
if (offset >= kctl->count) {
offset -= kctl->count;
continue;
}
for (jidx = offset; jidx < kctl->count; jidx++) {
snd_ctl_build_ioff(&id, kctl, jidx);
if (copy_to_user(list.pids + list.used, &id,
sizeof(id))) {
err = -EFAULT;
goto out;
}
list.used++;
if (!--space)
goto out;
}
offset = 0;
}
}
out:
up_read(&card->controls_rwsem);
if (!err && copy_to_user(_list, &list, sizeof(list)))
err = -EFAULT;
+++ copying list back to userspace leaks a pointer at least
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
)
{
struct snd_ctl_file *ctl;
struct snd_card *card;
struct snd_kctl_ioctl *p;
void __user *argp = (void __user *)arg;
int __user *ip = argp;
int err;
ctl = file->private_data;
card = ctl->card;
if (snd_BUG_ON(!card))
return -ENXIO;
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
case SNDRV_CTL_IOCTL_CARD_INFO:
return snd_ctl_card_info(card, ctl, cmd, argp);
case SNDRV_CTL_IOCTL_ELEM_LIST:
return snd_ctl_elem_list(card, argp);
3 ======================================
struct snd_emu10k1_fx8010_info {
unsigned int internal_tram_size; /* in samples */
unsigned int external_tram_size; /* in samples */
char fxbus_names[16][32]; /* names of FXBUSes */
char extin_names[16][32]; /* names of external inputs */
char extout_names[32][32]; /* names of external outputs */
unsigned int gpr_controls; /* count of GPR controls */
};
+++ note the strings/names are fixed lengths of 32
/usr/src/linux-source-4.14/sound/pci/emu10k1/emufx.c
static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
case SNDRV_EMU10K1_IOCTL_PVERSION:
emu->support_tlv = 1;
return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp);
case SNDRV_EMU10K1_IOCTL_INFO:
info = kmalloc(sizeof(*info), GFP_KERNEL);
+++ note kmalloc
if (!info)
return -ENOMEM;
snd_emu10k1_fx8010_info(emu, info);
if (copy_to_user(argp, info, sizeof(*info))) {
kfree(info);
return -EFAULT;
}
kfree(info);
return 0;
...
static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_info *info)
{
char **fxbus, **extin, **extout;
unsigned short fxbus_mask, extin_mask, extout_mask;
int res;
info->internal_tram_size = emu->fx8010.itram_size;
info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
fxbus = fxbuses;
extin = emu->audigy ? audigy_ins : creative_ins;
extout = emu->audigy ? audigy_outs : creative_outs;
fxbus_mask = emu->fx8010.fxbus_mask;
extin_mask = emu->fx8010.extin_mask;
extout_mask = emu->fx8010.extout_mask;
for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
}
+++ info leaks. the copy_strings partially fill the structure/strings which
+++ are 32 bytes. the remainder of the string is uninitialized (from kmalloc)
...
static void copy_string(char *dst, char *src, char *null, int idx)
{
if (src == NULL)
sprintf(dst, "%s %02X", null, idx);
else
strcpy(dst, src);
}
4 ====================================
struct inquiry_data {
bdaddr_t bdaddr;
__u8 pscan_rep_mode;
__u8 pscan_period_mode;
__u8 pscan_mode;
__u8 dev_class[3];
__le16 clock_offset;
__s8 rssi;
__u8 ssp_mode;
};
a
/usr/src/linux-source-4.14/net/bluetooth/hci_core.c
static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
{
struct discovery_state *cache = &hdev->discovery;
struct inquiry_info *info = (struct inquiry_info *) buf;
struct inquiry_entry *e;
int copied = 0;
list_for_each_entry(e, &cache->all, all) {
struct inquiry_data *data = &e->data;
if (copied >= num)
break;
bacpy(&info->bdaddr, &data->bdaddr);
info->pscan_rep_mode = data->pscan_rep_mode;
info->pscan_period_mode = data->pscan_period_mode;
info->pscan_mode = data->pscan_mode;
memcpy(info->dev_class, data->dev_class, 3);
info->clock_offset = data->clock_offset;
+++ note rssi and ssp_mode fields are not filled
+++ because this is from a kmalloc, it is left uninitialized. hence an infoleak
info++;
copied++;
}
...
/* cache_dump can't sleep. Therefore we allocate temp buffer and then
* copy it to the user space.
*/
buf = kmalloc(sizeof(struct inquiry_info) * max_rsp, GFP_KERNEL);
+++ note kmalloc
if (!buf) {
err = -ENOMEM;
goto done;
}
hci_dev_lock(hdev);
ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf);
hci_dev_unlock(hdev);
BT_DBG("num_rsp %d", ir.num_rsp);
if (!copy_to_user(ptr, &ir, sizeof(ir))) {
ptr += sizeof(ir);
if (copy_to_user(ptr, buf, sizeof(struct inquiry_info) *
ir.num_rsp))
err = -EFAULT;
} else
err = -EFAULT;
kfree(buf);
5 ========================================================
/usr/src/linux-source-4.14/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c
buf = vmalloc(mr->buffer_size);
if (!buf) {
DBG("vmalloc failed\n");
return -ENOMEM;
}
r = display->driver->memory_read(display, buf, mr->buffer_size,
mr->x, mr->y, mr->w, mr->h);
+++ r returns the amount of memory read. can be less than buffer_size
if (r > 0) {
if (copy_to_user(mr->buffer, buf, mr->buffer_size))
+++ copies the entire buffer
+++ hence, an infoleak from unitialised memory from vmalloc
r = -EFAULT;
}
vfree(buf);
6 ==============================================
/usr/src/linux-source-4.14/arch/ia64/sn/kernel/sn2/sn_hwperf.c
a
static long sn_hwperf_ioctl(struct file *fp, u32 op, unsigned long arg)
{
struct sn_hwperf_ioctl_args a;
...
r = copy_from_user(&a, (const void __user *)arg,
sizeof(struct sn_hwperf_ioctl_args));
if (r != 0) {
r = -EFAULT;
goto error;
}
...
if (a.ptr) {
p = vmalloc(a.sz);
if (!p) {
r = -ENOMEM;
goto error;
}
}
if (op & SN_HWPERF_OP_MEM_COPYIN) {
r = copy_from_user(p, (const void __user *)a.ptr, a.sz);
if (r != 0) {
r = -EFAULT;
goto error;
}
}
...
switch (op) {
case SN_HWPERF_GET_CPU_INFO:
...
case SN_HWPERF_GET_MMRS:
case SN_HWPERF_SET_MMRS:
case SN_HWPERF_OBJECT_DISTANCE:
op_info.p = p;
op_info.a = &a;
op_info.v0 = &v0;
op_info.op = op;
r = sn_hwperf_op_cpu(&op_info);
if (r) {
r = sn_hwperf_map_err(r);
a.v0 = v0;
goto error;
}
break;
default:
/* all other ops are a direct SAL call */
r = ia64_sn_hwperf_op(sn_hwperf_master_nasid, op,
a.arg, a.sz, (u64) p, 0, 0, &v0);
if (r) {
r = sn_hwperf_map_err(r);
goto error;
}
a.v0 = v0;
break;
}
+++ if any of the above cases don't fully overwrite the memory buffer from
+++ vmalloc, then we might have uninitialized memory and hence an infoleak
if (op & SN_HWPERF_OP_MEM_COPYOUT) {
r = copy_to_user((void __user *)a.ptr, p, a.sz);
if (r != 0) {
r = -EFAULT;
goto error;
}
}
=======================================================
Thursday, 6 September 2018
Subscribe to:
Posts (Atom)
Exploiting the Lorex 2K Indoor Wifi at Pwn2Own Ireland
Introduction In October InfoSect participated in Pwn2Own Ireland 2024 and successfully exploited the Sonos Era 300 smart speaker and Lor...
-
InfoSect has always been committed to fostering diversity and inclusion within the cybersecurity industry, with a special focus on encourag...
-
Summary This is the next part of the C++ memory corruption series*. In this post, we'll look at corrupting the std:string object in L...
-
Syed Faraz Abrar @farazsth98 Summary In this blog post, I will provide some details on how the Chromium developers implemente...