Skip to content

Instantly share code, notes, and snippets.

@xWTF
Last active February 11, 2024 15:44
Show Gist options
  • Save xWTF/2f74d78da32e361a883f440d19e705d9 to your computer and use it in GitHub Desktop.
Save xWTF/2f74d78da32e361a883f440d19e705d9 to your computer and use it in GitHub Desktop.
Access your Pageant from WSL 1

Here's an extremely simple way to access your Pageant SSH agent (such as the GPG one, so you can use your Yubikey or whatever in WSL) from WSL 1.

Install

Assume you're using the root account.

# In WSL
curl -Lo yolo-ssh-agent.go https://gist.github.com/xWTF/2f74d78da32e361a883f440d19e705d9/raw/yolo-ssh-agent.go
mkdir -p ~/.local/
go build -o ~/.local/yolo-ssh-agent.exe yolo-ssh-agent.go
rm yolo-ssh-agent.go

curl -Lo /etc/init.d/yolo-ssh-agent https://gist.github.com/xWTF/2f74d78da32e361a883f440d19e705d9/raw/yolo-ssh-agent
chmod +x /etc/init.d/yolo-ssh-agent

echo 'service yolo-ssh-agent start >/dev/null 2>&1
export SSH_AUTH_SOCK="/tmp/yolo-ssh-agent.sock"
' >> ~/.bashrc
exit

# In Windows
wsl --shutdown
wsl
#!/bin/sh
### BEGIN INIT INFO
# Provides: yolo-pageant
### END INIT INFO
. /lib/lsb/init-functions
NAME="yolo-ssh-agent"
PIDFILE="/var/run/$NAME.pid"
DAEMON="/usr/bin/socat"
DAEMON_ARGS="UNIX-LISTEN:/tmp/yolo-ssh-agent.sock,fork EXEC:/root/.local/yolo-ssh-agent.exe"
case "$1" in
start)
rm -f /tmp/yolo-ssh-agent.sock
log_daemon_msg "Starting $NAME service" "$NAME"
start-stop-daemon --start --background --pidfile "$PIDFILE" --make-pidfile --exec $DAEMON -- $DAEMON_ARGS
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $NAME service" "$NAME"
start-stop-daemon --stop --pidfile "$PIDFILE" --retry 10
log_end_msg $?
;;
status)
status_of_proc -p "$PIDFILE" "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|status|restart}"
exit 1
;;
esac
exit 0
/**
* Get updates from https://gist.github.com/xWTF/2f74d78da32e361a883f440d19e705d9
*
* Copyright © 2024 xWTF
* This work is free. You can redistribute it and/or modify it under the
* terms of the Do What The Fuck You Want To Public License, Version 2,
* as published by Sam Hocevar. See the COPYING file or http://www.wtfpl.net/
* for more details.
*/
// go build -o yolo-ssh-agent.exe yolo-ssh-agent.go
package main
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"os"
"reflect"
"syscall"
"time"
"unsafe"
)
const (
AGENT_IDENTIFIER = "Pageant"
AGENT_MAX_MSGLEN = 8192
AGENT_COPYDATA_ID = 0x804e50ba
WM_COPYDATA = 0x004A
)
var (
dllUser32 = syscall.NewLazyDLL("user32.dll")
procFindWindow = dllUser32.NewProc("FindWindowW")
procSendMessage = dllUser32.NewProc("SendMessageW")
)
type COPYDATASTRUCT struct {
dwData uintptr
cbData uint32
lpData uintptr
}
type sliceHack []byte
func (m *sliceHack) assign(len int, data uintptr) {
h := (*reflect.SliceHeader)(unsafe.Pointer(m))
h.Len = len
h.Cap = len
h.Data = data
}
func main() {
// Find Pageant window
u16str, _ := syscall.UTF16PtrFromString(AGENT_IDENTIFIER)
var hwnd uintptr
for tries := 0; hwnd == 0; tries++ {
hwnd, _, _ = procFindWindow.Call(uintptr(unsafe.Pointer(u16str)), uintptr(unsafe.Pointer(u16str)))
if tries > 50 { // 10 seconds
panic("Could not find Pageant")
}
time.Sleep(200 * time.Millisecond)
}
// Map shared memory
shmName := fmt.Sprintf("YOLO-PageantShm-%d", os.Getpid())
u16str, _ = syscall.UTF16PtrFromString(shmName)
hMap, err := syscall.CreateFileMapping(syscall.InvalidHandle, nil, syscall.PAGE_READWRITE, 0, AGENT_MAX_MSGLEN, u16str)
if err != nil {
panic(err)
}
defer syscall.CloseHandle(hMap)
rpMap, err := syscall.MapViewOfFile(hMap, syscall.FILE_MAP_READ|syscall.FILE_MAP_WRITE, 0, 0, AGENT_MAX_MSGLEN)
if err != nil {
panic(err)
}
defer syscall.UnmapViewOfFile(rpMap)
var shm sliceHack
shm.assign(AGENT_MAX_MSGLEN, rpMap)
// Allocate CDS
bMapName := make([]byte, len(shmName)+1)
copy(bMapName, shmName)
pCDS := uintptr(unsafe.Pointer(&COPYDATASTRUCT{
dwData: AGENT_COPYDATA_ID,
cbData: uint32(len(bMapName)),
lpData: uintptr(unsafe.Pointer(unsafe.SliceData(bMapName))),
}))
// Forward messages
reader := bufio.NewReader(os.Stdin)
for {
// read 4 bytes prefixed bufLen
if _, err := io.ReadFull(reader, shm[:4]); err != nil {
if err == io.EOF {
break
}
panic(err)
}
// request stdin -> shm
if l := binary.BigEndian.Uint32(shm[:4]); l > AGENT_MAX_MSGLEN-4 {
panic("request message too long")
} else if _, err := io.ReadFull(reader, shm[4:4+l]); err != nil {
panic(err)
}
// signal agent
if result, _, _ := procSendMessage.Call(hwnd, WM_COPYDATA, 0, pCDS); result == 0 {
panic("SendMessage failed")
}
// response shm -> stdout
if l := binary.BigEndian.Uint32(shm[:4]); l > AGENT_MAX_MSGLEN-4 {
panic("response message too long")
} else if _, err := os.Stdout.Write(shm[:4+l]); err != nil {
if err == io.EOF {
break
}
panic(err)
}
os.Stdout.Sync()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment