winchecksec
winchecksec copied to clipboard
Pass a directory as an argument
Hi! It would be nice if we could pass directory as an <file> / <dir>
argument. A lot of times when we are looking for a potential target in exploitable binary, we look in all external PE inside the target installation and the dependencies.
Right now, we have to check each PE separately for our $TARGET (manually through cli or via bash script). A more natively supported way would be awesome, something like:
> C:\winchecksec.exe -d path/to/dir/*.dll # does the sec check on all .dll
> C:\winchecksec.exe -d path/to/dir/*.exe # does the sec check on all .exe
Searched the repo but couldn't find this option.
Hotfix for now:
$ for FILE in *; do ./winchecksec $FILE; done # ugly, but it works
I'm not sure I understand: does winchecksec *.{exe,dll}
not work for you?
We check every file passed into us, so a normal glob should work just fine. Could you make sure that you're running the latest release (3.0.2)?
Nop, that doesn't work.
$ winchecksec .{exe,dll}
Couldn't load file; corrupt or not a PE?
Syntax : /Users/hduraki/utils/winchecksec/winchecksec [--json] <file [file ...]>
Example: /Users/hduraki/utils/winchecksec/winchecksec --json doom2.exe
-j/--json will output JSON to stdout
Actually tried it different ways in both MacOS/Windows/Linux but same results.
Edit: Yes I'm on latest release.
$ winchecksec -V
Winchecksec version 3.0.2
Could you make sure you ran the right glob? It needs to be *.{exe,dll}
not .{exe,dll}
-- the latter expands to .exe .dll
, which probably won't match any real files.
I'll test locally in a moment, but I just visually confirmed from the source code that we do indeed check every file passed to us:
for (auto path = std::next(cmdl.begin()); path != cmdl.end(); ++path) {
try {
checksec::Checksec csec(*path);
if (json) {
results.push_back(csec);
} else {
std::cout << "Results for: " << *path << '\n';
std::cout << csec << '\n';
}
} catch (checksec::ChecksecError& error) {
std::cerr << error.what() << '\n';
usage(argv);
return 2;
} catch (...) {
std::cerr << "General error" << '\n';
usage(argv);
return 3;
}
}
Sorry for that typo. Still the same tho.
$ winchecksec *.{exe,dll}
zsh: no matches found: *.exe
So I thought it must be zsh quirk? But then with sh:
/bin/sh
sh-3.2$ /Users/hduraki/utils/winchecksec/winchecksec *.{exe,dll}
Couldn't load file; corrupt or not a PE?
Syntax : /Users/hduraki/utils/winchecksec/winchecksec [--json] <file [file ...]>
Example: /Users/hduraki/utils/winchecksec/winchecksec --json doom2.exe
-j/--json will output JSON to stdout
And bourne shell as well:
bash-3.2$ /Users/hduraki/utils/winchecksec/winchecksec *.{exe,dll}
Couldn't load file; corrupt or not a PE?
Syntax : /Users/hduraki/utils/winchecksec/winchecksec [--json] <file [file ...]>
Example: /Users/hduraki/utils/winchecksec/winchecksec --json doom2.exe
-j/--json will output JSON to stdout
Tested on latest MacOS.
@woodruffw could you replicate?
Windows hotfix for now:
> for %i in (*.*) do winchecksec.exe %i
Sorry, this fell of my radar completely. Trying now...
I can't reproduce this locally:
$ ./winchecksec ../test/assets/32/*.exe
...audits all of the EXEs under that directory.
Example output:
Results for: ../test/assets/32/pegoat-authenticode.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "Present"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-ineffective-cfg-no-dynamicbase.exe
Dynamic Base : "NotPresent"
ASLR : "NotPresent"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-no-cetcompat.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-no-cfg.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-no-dynamicbase.exe
Dynamic Base : "NotPresent"
ASLR : "NotPresent"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-no-gs.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-no-integritycheck.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-no-nxcompat.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "NotPresent"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-no-safeseh.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat-yes-cfg.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "Present"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Results for: ../test/assets/32/pegoat.exe
Dynamic Base : "Present"
ASLR : "Present"
High Entropy VA : "NotPresent"
Force Integrity : "NotPresent"
Isolation : "Present"
NX : "Present"
SEH : "Present"
CFG : "NotPresent"
RFG : "NotPresent"
SafeSEH : "NotPresent"
GS : "Present"
Authenticode : "NotPresent"
.NET : "NotPresent"
Looking at your globs: I'm not sure if you were running in a directory that had any executables or other auditable files in it.
The ZSH error is the one ZSH gives you when a glob expands to nothing; Bash and the standard sh
both return the glob itself if nothing matches (which then naturally fails, since *.{exe,dll}
isn't a file). So you should double check that your directory actually has the files you expect in it, and that they're visible to your shell (i.e., not hidden behind some weird permission or ACL).
Ping: were you able to resolve this? I can confirm that globs work correctly for me locally, on all of Winchecksec's supported hosts.
...except for Windows of course, since Windows doesn't have shell-native globbing.
Looks like I misread this a little: if you're still seeing the problem on Windows, it's because the Windows basic shell (cmd.exe
) just doesn't support globbing. There isn't much we can do about that on Winchecksec's side.
Yeah I meant for WinNT. I think the first post shows a viable option. Just scan for globs through the app interface and voila :)