-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Max
authored and
Max
committed
Jan 24, 2023
0 parents
commit d24e8d0
Showing
8 changed files
with
344 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[*] | ||
charset = utf-8 | ||
end_of_line = lf | ||
insert_final_newline = true | ||
trim_trailing_whitespace = true | ||
|
||
[*.v] | ||
indent_style = tab | ||
indent_size = 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
* text=auto eol=lf | ||
*.bat eol=crlf | ||
|
||
**/*.v linguist-language=V | ||
**/*.vv linguist-language=V | ||
**/*.vsh linguist-language=V | ||
**/v.mod linguist-language=V |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Binaries for programs and plugins | ||
main | ||
vlinpeas | ||
*.exe | ||
*.exe~ | ||
*.so | ||
*.dylib | ||
*.dll | ||
|
||
# Ignore binary output folders | ||
bin/ | ||
|
||
# Ignore common editor/system specific metadata | ||
.DS_Store | ||
.idea/ | ||
.vscode/ | ||
*.iml | ||
|
||
# ENV | ||
.env | ||
|
||
# vweb and database | ||
*.db | ||
*.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# What is this? | ||
This is a wrapper for linpeas json files. The intent of which is to allow for more programability for linpeas output. | ||
|
||
I am primarily developing in a linux environment. So it might not work quite as well for Windows based environments | ||
|
||
# Technical Limitations | ||
## Winpeas Uncertainty | ||
I can confirm this will work in both MacOS and Linux using macpeas and linpeas respectively | ||
|
||
I am uncertain how effective this will be with winpeas. I use nix environments at home so I do not know how well this will work when running winpeas and parsing its output. Theoretically it should work as I am only parsing the output unless there is something with the underlying implementation of V right now that makes it difficult to run on Windows. | ||
|
||
## JSON Parsing Capabilities | ||
The current V documentation organizes json as structs where each field in a struct is the json key. | ||
|
||
The issue with this is that I will need to explicitly declare every JSON field name. That can be a long and tedious process, especially when something follows a defined structure in JSON. | ||
|
||
The linpeas JSON output seems to follow the following structure: | ||
``` | ||
{ | ||
'Scan1': { | ||
sections: { | ||
}, | ||
lines: [ | ||
{ | ||
}, | ||
], | ||
'infos': [] | ||
}, | ||
'Scan2': { | ||
sections: { | ||
'Scan': { | ||
sections: {}, | ||
lines: [], | ||
infos: [ | ||
'string' | ||
] | ||
} | ||
}, | ||
lines: [ | ||
{ | ||
'raw_text': 'raw text with shell color encoding text', | ||
'colors': { | ||
'DETECTED_COLOR': [ | ||
'optional text in dark grey, blue, green, redyellow,red. This will not be here if there is nothing detected', | ||
], | ||
}, | ||
'clean_text': 'text without shell encoding text' | ||
}, | ||
], | ||
'infos': [ | ||
'URL string' | ||
] | ||
}, | ||
} | ||
``` | ||
|
||
So all in all, it is not the most complex structure. It is very repetative. The issue though is that the Scan key will have a different name every time. | ||
|
||
There is currently an experimental json library in the core vlib (json2) but it is experimental and is prone to error. | ||
|
||
Honestly knowing how V works and how the structure of a JSON file for linpeas is set up, I really probably should just use the abstract syntax tree. But that would require learning. Gross. | ||
|
||
I also had a shower thought that really all it could be is a map[string]map[string][]string where | ||
1. The first map is the section name | ||
1. The second map is the color | ||
1. The strings array are all the findings of the color | ||
The only problem with this shower thought is if the scan format changes, it isn't exactly the most extensible for other formats. | ||
|
||
# Install | ||
1. vpm | ||
> v install trinitok.vlinpeas | ||
# Use | ||
## Prerequisite | ||
1. Run linpeas and send the output to a file | ||
1. https://github.com/carlospolop/PEASS-ng | ||
1. Use the `peass2json.py` program to turn linpeas into json | ||
## Usage | ||
``` | ||
import trinitok.vlinpeas | ||
peass2json_output := 'path_to_peass2json.py_output.text' | ||
// Parse the output | ||
linpeas_out := linpeas_analyzer.decode_linpeas_json(peass2json_output) | ||
// Retrieve all REDYELLOW text (95% PE vectors) | ||
crit_findings := linpeas_out.retrieve_critical_findings() | ||
// Retrieve all BLUE text (because why not?) | ||
blue_findings := linpeas_out. | ||
``` | ||
# Testing | ||
You can definitely try running the tests. They will fail because I did not include a local file from the linpeas output. You will likely want to add a peass2json output file and change the name in the `analyzer_test.v` | ||
|
||
# TODO | ||
1. Return more than just the first instance of the linpeas keywords | ||
1. Rewrite the peass2json in V | ||
1. Allow for more ways to analyze output of lin/mac/win-peas | ||
1. Optimize parser to not be so jank (slightly dependent on V making advancements on json parsing) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
module linpeas_analyzer | ||
|
||
struct LinpeasRetrievedFindings { | ||
name string | ||
clean_text string | ||
findings []string | ||
} | ||
|
||
// Retrieve all the text from linpeas findings that were | ||
// marked as redyellow output in the terminal | ||
pub fn (linpeas_findings LinpeasFindings) retrieve_critical_findings() []LinpeasRetrievedFindings { | ||
scans := linpeas_findings.scans | ||
crit_findings := | ||
retrieve_linpeas_finding_lines_with_color('REDYELLOW', scans) | ||
return crit_findings | ||
} | ||
|
||
// Retrieve all lines of text from linpeas findings that match the color_id passed. | ||
// Must match a valid color from Linpeas | ||
pub fn (linpeas_findings LinpeasFindings) retrieve_findings_with_color_id(color_id string) []LinpeasRetrievedFindings { | ||
scans := linpeas_findings.scans | ||
crit_findings := | ||
retrieve_linpeas_finding_lines_with_color(color_id, scans) | ||
return crit_findings | ||
} | ||
|
||
// Uses depth first search in order to obtain all the color_name texts from LinpeasLinesNode | ||
fn retrieve_linpeas_finding_lines_with_color(color_name string, scan_nodes []LinpeasScanNode) []LinpeasRetrievedFindings { | ||
mut ret_arr := []LinpeasRetrievedFindings | ||
for scan in scan_nodes { | ||
sections := scan.sections | ||
if sections.sub_scan.len > 0 { | ||
ret_arr << retrieve_linpeas_finding_lines_with_color(color_name, sections.sub_scan) | ||
} | ||
lines := scan.lines | ||
if color_name in lines.colors.keys() { | ||
ret_arr << LinpeasRetrievedFindings { | ||
name: scan.name | ||
clean_text: lines.clean_text | ||
findings: lines.colors[color_name] | ||
} | ||
} | ||
} | ||
|
||
return ret_arr | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
module linpeas_analyzer | ||
|
||
import os | ||
import x.json2 | ||
|
||
struct LinpeasFindings { | ||
scans []LinpeasScanNode | ||
} | ||
|
||
struct LinpeasScanNode { | ||
name string | ||
sections LinpeasSectionNode | ||
lines LinpeasLineNode | ||
infos []string | ||
} | ||
|
||
struct LinpeasSectionNode { | ||
sub_scan []LinpeasScanNode | ||
} | ||
|
||
struct LinpeasLineNode { | ||
colors map[string][]string | ||
clean_text string | ||
} | ||
|
||
// fn init() { | ||
// println('probably want to install this here') | ||
// os.execute_or_panic('curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh > linpeas.out') | ||
// } | ||
|
||
pub fn decode_linpeas_json(filename string) LinpeasFindings { | ||
buf := os.read_file(filename) or { | ||
println('error reading linpeas.json file') | ||
panic(err) | ||
} | ||
|
||
test_out := json2.raw_decode(buf) or { | ||
println('issue trying to decode linpeas.json file') | ||
unsafe { buf.free() } | ||
panic(err) | ||
} | ||
unsafe { buf.free() } | ||
product := test_out.as_map() | ||
data := product as map[string]json2.Any | ||
linpeas_out := parse_scan_nodes(data) | ||
|
||
return LinpeasFindings { | ||
scans: linpeas_out | ||
} | ||
} | ||
|
||
fn parse_json_str_to_map(json_str string) map[string]json2.Any { | ||
test_basic_info_values := json2.raw_decode(json_str) or { | ||
println('error trying to decode values for json string: ${json_str}') | ||
panic(err) | ||
} | ||
product_sections := test_basic_info_values.as_map() | ||
data_sections := product_sections as map[string]json2.Any | ||
|
||
return data_sections | ||
} | ||
|
||
fn parse_scan_nodes(scan_node map[string]json2.Any) []LinpeasScanNode { | ||
mut ret_arr := []LinpeasScanNode {} | ||
for scan_key in scan_node.keys() { | ||
// convert to json str | ||
scan_children_str := scan_node[scan_key].str() | ||
mapped_scan_node_children := parse_json_str_to_map(scan_children_str) | ||
new_scan_node := LinpeasScanNode { | ||
name: scan_key | ||
sections: process_sections(mapped_scan_node_children['sections']) | ||
lines: process_lines(mapped_scan_node_children['lines']) | ||
infos: process_infos(mapped_scan_node_children['infos']) | ||
} | ||
ret_arr << new_scan_node | ||
} | ||
|
||
return ret_arr | ||
} | ||
|
||
fn process_sections(scan_section json2.Any) LinpeasSectionNode { | ||
mapped_section_json := scan_section.as_map() | ||
if mapped_section_json.len == 0 { | ||
return LinpeasSectionNode {} | ||
} | ||
else { | ||
return LinpeasSectionNode { | ||
sub_scan: parse_scan_nodes(mapped_section_json) | ||
} | ||
} | ||
} | ||
|
||
fn process_lines_colors(mapped_value_colors map[string]json2.Any) map[string][]string { | ||
mut ret_map := map[string][]string | ||
// for each string key in the colors take the color map key, then for the array loop over that and convert each value to string | ||
for key in mapped_value_colors.keys() { | ||
colors_arr := mapped_value_colors[key].arr() | ||
mut transformed_colors_arr := []string | ||
// loop over each json2.Any value and convert each individually to string | ||
for i := 0; i < colors_arr.len; i ++ { | ||
orig_item := colors_arr[i] | ||
transformed_colors_arr << orig_item.str() | ||
} | ||
ret_map[key] << transformed_colors_arr | ||
} | ||
|
||
return ret_map | ||
} | ||
|
||
fn process_lines(scan_lines json2.Any) LinpeasLineNode { | ||
lines := scan_lines.as_map() | ||
mut ret_map := map[string][]string | ||
mut ret_clean_text := '' | ||
// reeeee super inefficient | ||
// for each value in lines | ||
for value in lines.values() { | ||
// []json2.Any cannot convert to []string so I have to go over every array and every value in the array and convert them to an array of strings | ||
mapped_values := value.as_map() | ||
mapped_value_colors := mapped_values['colors'].as_map() | ||
ret_clean_text = mapped_values['clean_text'].str() | ||
// get the colors | ||
ret_map = process_lines_colors(mapped_value_colors) | ||
} | ||
return LinpeasLineNode { | ||
colors: ret_map | ||
clean_text: ret_clean_text | ||
} | ||
} | ||
|
||
fn process_infos(scan_infos json2.Any) [] string { | ||
scan_infos_arr := scan_infos.arr() | ||
mut ret_arr := []string | ||
for value in scan_infos_arr { | ||
ret_arr << scan_infos_arr.str() | ||
} | ||
return ret_arr | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module tests | ||
|
||
import linpeas_analyzer | ||
|
||
fn test_parse_and_crit_findings() { | ||
linpeas_out_file := 'linpeas.json' | ||
|
||
linpeas_out := linpeas_analyzer.decode_linpeas_json(linpeas_out_file) | ||
crit_findings := linpeas_out.retrieve_critical_findings() | ||
|
||
println('here are the critical linpeas findings: ${crit_findings}') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Module { | ||
name: 'vlinpeas' | ||
description: 'Linpeas JSON output parser in V Lang' | ||
version: '0.1' | ||
license: 'MIT' | ||
dependencies: [] | ||
} |