Skip to content

Commit fa2da23

Browse files
committed
Merge branch 'release/v1.1.0' into main
2 parents 4a069da + e33823f commit fa2da23

File tree

11 files changed

+501
-84
lines changed

11 files changed

+501
-84
lines changed

.github/workflows/go.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ jobs:
1616
strategy:
1717
matrix:
1818
go: ['1.19', '1.20', '1.21']
19+
os: [ 'linux', 'windows', 'darwin' ]
20+
arch: [ 'amd64', 'arm64' ]
1921
steps:
2022
- uses: actions/checkout@v3
2123

@@ -25,4 +27,7 @@ jobs:
2527
go-version: ${{ matrix.go }}
2628

2729
- name: Build
30+
env:
31+
GOOS: ${{ matrix.os }}
32+
GOARCH: ${{ matrix.arch }}
2833
run: go build -v ./...

README.md

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
1+
# htb-cli
2+
3+
![Workflows (main)](https://github.com/GoToolSharing/htb-cli/actions/workflows/go.yml/badge.svg?branch=main)
4+
![Workflows (dev)](https://github.com/GoToolSharing/htb-cli/actions/workflows/go.yml/badge.svg?branch=dev)
5+
6+
<div>
7+
<img alt="current version" src="https://img.shields.io/badge/linux-supported-success">
8+
<img alt="current version" src="https://img.shields.io/badge/windows-supported-success">
9+
<img alt="current version" src="https://img.shields.io/badge/mac-supported-success">
10+
<br>
11+
<img alt="amd64" src="https://img.shields.io/badge/amd64%20(x86__64)-supported-success">
12+
<img alt="arm64" src="https://img.shields.io/badge/arm64%20(aarch64)-supported-success">
13+
</div>
14+
15+
## Installation
16+
17+
`go install github.com/GoToolSharing/htb-cli@latest`
18+
19+
## Configuration
20+
21+
You must add a Hackthebox **App token** in the **HTB_TOKEN** environment variable (zshrc maybe).
22+
API Token can be find here : https://app.hackthebox.com/profile/settings => `Create App Token`
23+
24+
```
25+
export HTB_TOKEN=eyJ...
26+
```
27+
28+
## Helper
29+
130
```
231
This software, engineered using the Go programming language, serves to streamline and automate various tasks for the HackTheBox platform, enhancing user efficiency and productivity.
332
@@ -6,12 +35,13 @@ Usage:
635
736
Available Commands:
837
active Catalogue of active machines
9-
submit Submit credentials (User and Root Flags)
1038
help Help about any command
1139
info Showcase detailed machine information
12-
reset Reset a machine - [WIP]
40+
reset Reset a machine
1341
start Start a machine
42+
status Displays the status of HackTheBox servers
1443
stop Stop the current machine
44+
submit Submit credentials (User and Root Flags)
1545
1646
Flags:
1747
-h, --help help for htb-cli
@@ -21,58 +51,68 @@ Flags:
2151
Use "htb-cli [command] --help" for more information about a command.
2252
```
2353

24-
## Installation
25-
26-
`go install github.com/GoToolSharing/htb-cli@latest`
27-
28-
## Configuration
29-
30-
You must add a Hackthebox **App token** in the **HTB_TOKEN** environment variable (zshrc maybe).
31-
API Token can be find here : https://app.hackthebox.com/profile/settings => Create App Token
32-
33-
```
34-
export HTB_TOKEN=eyJ...
35-
```
36-
3754
## Start
3855

3956
```
40-
> htb-cli start -m Flight
57+
❯ htb-cli start -m Blue
58+
? The following machine was found : Blue Yes
4159
Machine deployed to lab.
4260
```
4361

4462
## Stop
4563

4664
```
47-
> htb-cli stop
65+
htb-cli stop
4866
Machine terminated.
4967
```
5068

5169
## Reset
5270

5371
```
54-
> htb-cli reset -m Flight
55-
Machine terminated.
72+
htb-cli reset
73+
CozyHosting will be reset in 1 minute.
5674
```
5775

5876
## Submit
5977

6078
This command allows to submit the user flag and the root flag of active and retired machines. The first argument is the flag and the second the difficulty /10.
6179

80+
### Submit machine flag
6281
```
63-
> htb-cli submit -f flag4testing -d 3
64-
82+
htb-cli submit -m SteamCloud -f flag4testing -d 3
83+
? The following machine was found : SteamCloud Yes
6584
SteamCloud user is now owned.
6685
```
6786

87+
### Submit challenge flag
88+
```
89+
❯ htb-cli submit -c Phonebook -f flag4testing -d 3
90+
? The following challenge was found : Phonebook Yes
91+
Incorrect flag
92+
```
93+
6894
## Info
6995

70-
By default the command shows the active machine.
96+
```
97+
❯ htb-cli info
98+
? Do you want to check for active machine ? Yes
99+
Name |OS |Active |Difficulty |Stars |IP |Status |Release
100+
Blue |Windows |0 |Easy |4.5 |10.10.10.40 |✅ User - ✅ Root |2017-07-28
101+
```
71102

72103
```
73-
> htb-cli info -m Zipper -m Sau
104+
❯ htb-cli info -m Zip -m pilgrimage
105+
? Do you want to check for active machine ? No
106+
? The following machine was found : Zipping Yes
107+
Name |OS |Active |Difficulty |Stars |FirstUserBlood |FirstRootBlood |Status |Release
108+
Zipping |Linux |✅ |Medium |4.1 |0H 15M 9S |1H 12M 28S |❌ User - ❌ Root |2023-08-26
109+
? The following machine was found : Pilgrimage Yes
110+
Pilgrimage |Linux |✅ |Easy |4.5 |0H 17M 0S |0H 20M 33S |✅ User - ✅ Root |2023-06-24
111+
```
74112

75-
Name |OS |Active |Difficulty |Stars |FirstUserBlood |FirstRootBlood |Status |Release
76-
Zipper |Linux |0 |Hard |4.5 |1H 57M 59S |2H 8M 18S |❌ User - ❌ Root |2018-10-20
77-
Sau |Linux |1 |Easy |4.6 |0H 8M 39S |0H 11M 40S |✅ User - ✅ Root |2023-07-08
113+
## Status
114+
115+
```
116+
❯ htb-cli status
117+
All Systems Operational
78118
```

cmd/info.go

Lines changed: 136 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,57 +8,163 @@ import (
88
"text/tabwriter"
99
"time"
1010

11+
"github.com/AlecAivazis/survey/v2"
1112
"github.com/GoToolSharing/htb-cli/utils"
1213
"github.com/kyokomi/emoji/v2"
1314
"github.com/spf13/cobra"
1415
)
1516

1617
var machineParam []string
18+
var challengeParam []string
19+
20+
func checkActiveMachine() {
21+
machine_id := utils.GetActiveMachineID(proxyParam)
22+
status := "Not defined"
23+
retired_status := "Not defined"
24+
if machine_id != "" {
25+
log.Println("Active machine found !")
26+
log.Println("Machine ID:", machine_id)
27+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.Debug)
28+
fmt.Fprintln(w, "Name\tOS\tActive\tDifficulty\tStars\tIP\tStatus\tRelease")
29+
url := "https://www.hackthebox.com/api/v4/machine/profile/" + machine_id
30+
resp, err := utils.HtbRequest(http.MethodGet, url, proxyParam, nil)
31+
if err != nil {
32+
log.Fatal(err)
33+
}
34+
info := utils.ParseJsonMessage(resp, "info")
35+
data := info.(map[string]interface{})
36+
if data["authUserInUserOwns"] == nil && data["authUserInRootOwns"] == nil {
37+
status = emoji.Sprint(":x:User - :x:Root")
38+
} else if data["authUserInUserOwns"] == true && data["authUserInRootOwns"] == nil {
39+
status = emoji.Sprint(":white_check_mark:User - :x:Root")
40+
} else if data["authUserInUserOwns"] == nil && data["authUserInRootOwns"] == true {
41+
status = emoji.Sprint(":x:User - :white_check_mark:Root")
42+
} else if data["authUserInUserOwns"] == true && data["authUserInRootOwns"] == true {
43+
status = emoji.Sprint(":white_check_mark:User - :white_check_mark:Root")
44+
}
45+
if data["retired"].(float64) == 0 {
46+
retired_status = emoji.Sprint(":white_check_mark:")
47+
} else {
48+
retired_status = emoji.Sprint(":x:")
49+
}
50+
t, err := time.Parse(time.RFC3339Nano, data["release"].(string))
51+
if err != nil {
52+
fmt.Println("Erreur when date parsing :", err)
53+
return
54+
}
55+
datetime := t.Format("2006-01-02")
56+
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", data["name"], data["os"], retired_status, data["difficultyText"], data["stars"], data["ip"], status, datetime)
57+
w.Flush()
58+
}
59+
}
1760

1861
var infoCmd = &cobra.Command{
1962
Use: "info",
2063
Short: "Showcase detailed machine information",
2164
Long: "Displays detailed information of the specified machines in a structured table.",
2265
Run: func(cmd *cobra.Command, args []string) {
23-
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.Debug)
24-
fmt.Fprintln(w, "Name\tOS\tActive\tDifficulty\tStars\tFirstUserBlood\tFirstRootBlood\tStatus\tRelease")
25-
status := "Not defined"
26-
log.Println(machineParam)
27-
for index, _ := range machineParam {
28-
machine_id := utils.SearchMachineIDByName(machineParam[index], proxyParam)
66+
if len(machineParam) > 0 && len(challengeParam) > 0 {
67+
fmt.Println("Error: You can only specify either -m or -c flags, not both.")
68+
cmd.Help()
69+
os.Exit(1)
70+
}
71+
var confirmation bool
72+
confirmation_message := "Do you want to check for active machine ?"
73+
prompt := &survey.Confirm{
74+
Message: confirmation_message,
75+
}
76+
if err := survey.AskOne(prompt, &confirmation); err != nil {
77+
log.Fatal(err)
78+
}
79+
if confirmation {
80+
checkActiveMachine()
81+
}
2982

30-
url := "https://www.hackthebox.com/api/v4/machine/profile/" + machine_id
31-
resp, err := utils.HtbRequest(http.MethodGet, url, proxyParam, nil)
32-
if err != nil {
33-
log.Fatal(err)
34-
}
35-
info := utils.ParseJsonMessage(resp, "info")
83+
// Machines search
84+
if len(machineParam) > 0 {
85+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.Debug)
86+
fmt.Fprintln(w, "Name\tOS\tActive\tDifficulty\tStars\tFirstUserBlood\tFirstRootBlood\tStatus\tRelease")
87+
status := "Not defined"
88+
retired_status := "Not defined"
89+
log.Println(machineParam)
90+
for index, _ := range machineParam {
91+
machine_id := utils.SearchItemIDByName(machineParam[index], proxyParam, "Machine")
3692

37-
data := info.(map[string]interface{})
38-
if data["authUserInUserOwns"] == nil && data["authUserInRootOwns"] == nil {
39-
status = emoji.Sprint(":x:User - :x:Root")
40-
} else if data["authUserInUserOwns"] == true && data["authUserInRootOwns"] == nil {
41-
status = emoji.Sprint(":white_check_mark:User - :x:Root")
42-
} else if data["authUserInUserOwns"] == nil && data["authUserInRootOwns"] == true {
43-
status = emoji.Sprint(":x:User - :white_check_mark:Root")
44-
} else if data["authUserInUserOwns"] == true && data["authUserInRootOwns"] == true {
45-
status = emoji.Sprint(":white_check_mark:User - :white_check_mark:Root")
46-
}
47-
t, err := time.Parse(time.RFC3339Nano, data["release"].(string))
48-
if err != nil {
49-
fmt.Println("Erreur when date parsing :", err)
50-
return
93+
url := "https://www.hackthebox.com/api/v4/machine/profile/" + machine_id
94+
resp, err := utils.HtbRequest(http.MethodGet, url, proxyParam, nil)
95+
if err != nil {
96+
log.Fatal(err)
97+
}
98+
info := utils.ParseJsonMessage(resp, "info")
99+
100+
data := info.(map[string]interface{})
101+
if data["authUserInUserOwns"] == nil && data["authUserInRootOwns"] == nil {
102+
status = emoji.Sprint(":x:User - :x:Root")
103+
} else if data["authUserInUserOwns"] == true && data["authUserInRootOwns"] == nil {
104+
status = emoji.Sprint(":white_check_mark:User - :x:Root")
105+
} else if data["authUserInUserOwns"] == nil && data["authUserInRootOwns"] == true {
106+
status = emoji.Sprint(":x:User - :white_check_mark:Root")
107+
} else if data["authUserInUserOwns"] == true && data["authUserInRootOwns"] == true {
108+
status = emoji.Sprint(":white_check_mark:User - :white_check_mark:Root")
109+
}
110+
if data["retired"].(float64) == 0 {
111+
retired_status = emoji.Sprint(":white_check_mark:")
112+
} else {
113+
retired_status = emoji.Sprint(":x:")
114+
}
115+
t, err := time.Parse(time.RFC3339Nano, data["release"].(string))
116+
if err != nil {
117+
fmt.Println("Erreur when date parsing :", err)
118+
return
119+
}
120+
datetime := t.Format("2006-01-02")
121+
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", data["name"], data["os"], retired_status, data["difficultyText"], data["stars"], data["firstUserBloodTime"], data["firstRootBloodTime"], status, datetime)
122+
w.Flush()
51123
}
52-
datetime := t.Format("2006-01-02")
53-
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", data["name"], data["os"], data["active"], data["difficultyText"], data["stars"], data["firstUserBloodTime"], data["firstRootBloodTime"], status, datetime)
54124
}
55-
w.Flush()
56125

126+
// Challenges search
127+
if len(challengeParam) > 0 {
128+
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.Debug)
129+
fmt.Fprintln(w, "Name\tCategory\tActive\tDifficulty\tStars\tSolves\tStatus\tRelease")
130+
status := "Not defined"
131+
retired_status := "Not defined"
132+
log.Println(challengeParam)
133+
for index, _ := range challengeParam {
134+
challenge_id := utils.SearchItemIDByName(challengeParam[index], proxyParam, "Challenge")
135+
log.Println("Challenge id:", challenge_id)
136+
url := "https://www.hackthebox.com/api/v4/challenge/info/" + challenge_id
137+
resp, err := utils.HtbRequest(http.MethodGet, url, proxyParam, nil)
138+
if err != nil {
139+
log.Fatal(err)
140+
}
141+
info := utils.ParseJsonMessage(resp, "challenge")
142+
data := info.(map[string]interface{})
143+
if data["authUserSolve"] == false {
144+
status = emoji.Sprint(":x:Flag")
145+
} else {
146+
status = emoji.Sprint(":white_check_mark:Flag")
147+
}
148+
if data["retired"].(float64) == 0 {
149+
retired_status = emoji.Sprint(":white_check_mark:")
150+
} else {
151+
retired_status = emoji.Sprint(":x:")
152+
}
153+
t, err := time.Parse(time.RFC3339Nano, data["release_date"].(string))
154+
if err != nil {
155+
fmt.Println("Erreur when date parsing :", err)
156+
return
157+
}
158+
datetime := t.Format("2006-01-02")
159+
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\t%v\t%v\t%v\n", data["name"], data["category_name"], retired_status, data["difficulty"], data["stars"], data["solves"], status, datetime)
160+
w.Flush()
161+
}
162+
}
57163
},
58164
}
59165

60166
func init() {
61167
rootCmd.AddCommand(infoCmd)
62168
infoCmd.Flags().StringSliceVarP(&machineParam, "machine", "m", []string{}, "Machine name")
63-
infoCmd.MarkFlagRequired("machine")
169+
infoCmd.Flags().StringSliceVarP(&challengeParam, "challenge", "c", []string{}, "Challenge name")
64170
}

cmd/reset.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
var resetCmd = &cobra.Command{
1313
Use: "reset",
14-
Short: "Reset a machine - [WIP]",
14+
Short: "Reset a machine",
1515
Long: "Initiates a reset request for the selected machine.",
1616
Run: func(cmd *cobra.Command, args []string) {
1717
machine_id := utils.GetActiveMachineID(proxyParam)

cmd/start.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var startCmd = &cobra.Command{
1616
Short: "Start a machine",
1717
Long: `Starts a Hackthebox machine specified in argument`,
1818
Run: func(cmd *cobra.Command, args []string) {
19-
machine_id := utils.SearchMachineIDByName(machineChoosen, proxyParam)
19+
machine_id := utils.SearchItemIDByName(machineChoosen, proxyParam, "Machine")
2020
log.Println("Machine ID :", machine_id)
2121
machine_type := utils.GetMachineType(machine_id, proxyParam)
2222
log.Println("Machine Type :", machine_type)

0 commit comments

Comments
 (0)