Skip to content

Commit

Permalink
Organizing the project & implementing ADIDNS viewer./g
Browse files Browse the repository at this point in the history
  • Loading branch information
Macmod committed Jun 25, 2024
1 parent 809b5a7 commit 128a27d
Show file tree
Hide file tree
Showing 33 changed files with 2,464 additions and 512 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* 🗑️ Supports searching deleted & recycled objects
* 📁 Supports exporting specific subtrees of the directory into JSON files
* 📜 GPO Viewer
* 🌐 ADIDNS Viewer
* 🕹️ Interactive userAccountControl editor
* 🔥 Interactive DACL editor
* 🧦 SOCKS support
Expand All @@ -47,6 +48,12 @@ $ go install .

**Bind with username and password**

```bash
$ godap <hostname or IP> -u <username> -p <password> -d <domain>
```

or

```bash
$ godap <hostname or IP> -u <username>@<domain> -p <password>
```
Expand Down Expand Up @@ -131,7 +138,7 @@ You can also change the address of your proxy using the `l` keybinding.
| <kbd>l</kbd> | Global | Change current server address & credentials |
| <kbd>Ctrl</kbd> + <kbd>r</kbd> | Global | Reconnect to the server |
| <kbd>Ctrl</kbd> + <kbd>u</kbd> | Global | Upgrade connection to use TLS (with StartTLS) |
| <kbd>Ctrl</kbd> + <kbd>f</kbd> | LDAP Explorer & Object Search pages | Open the finder to search for cached objects & attributes with regex |
| <kbd>Ctrl</kbd> + <kbd>f</kbd> | Explorer & Search pages | Open the finder to search for cached objects & attributes with regex |
| Right Arrow | Explorer panel | Expand the children of the selected object |
| Left Arrow | Explorer panel | Collapse the children of the selected object |
| <kbd>r</kbd> | Explorer panel | Reload the attributes and children of the selected object |
Expand All @@ -155,6 +162,8 @@ You can also change the address of your proxy using the `l` keybinding.
| <kbd>Ctrl</kbd> + <kbd>e</kbd> | DACL entries panel | Edit the selected ACE of the current DACL |
| <kbd>Delete</kbd> | DACL entries panel | Deletes the selected ACE of the current DACL |
| <kbd>Ctrl</kbd> + <kbd>s</kbd> | GPO page | Export the current GPOs and their links into a JSON file |
| <kbd>Ctrl</kbd> + <kbd>s</kbd> | DNS zones panel | Export the selected zones and their child DNS nodes into a JSON file |
| <kbd>r</kbd> | DNS zones panel | Reload the nodes of the selected zone / the records of the selected node |
| <kbd>h</kbd> | Global | Show/hide headers |
| <kbd>q</kbd> | Global | Exit the program |

Expand Down
4 changes: 2 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# TODO (priority)

* Feature: Support deleting specific attribute values (not the entire attribute)
* Feature: View/modify ADIDNS dnsZones and dnsNodes
* Feature: Modify ADIDNS dnsZones and dnsNodes
* Feature: Options to manipulate (edit/create/delete) gpLinks visually
* Feature: Load initial cache from file

Expand All @@ -10,7 +10,7 @@
* Feature: Improve object creation form (implement customizations)
* Feature: Custom themes
* Feature: Customizable keybindings
* Refactor: Improve the organization of files, functions and structures (among other ideas, remove the "utils" package)
* Wish: Add tests for core functions to make sure everything is in order
* Wish: Mini tool to convert godap exports into bloodhound dumps
* Wish: Monitor object for real-time changes (DirSync/SyncRepl)
* Wish: Some way to copy data from panels (not implemented in tview, only for the "textarea" primitive)
Expand Down
61 changes: 61 additions & 0 deletions godap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package main

import (
"fmt"
"github.com/Macmod/godap/v2/tui"
"github.com/spf13/cobra"
)

func main() {
rootCmd := &cobra.Command{
Use: "godap <server address>",
Short: "A complete TUI for LDAP.",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
tui.LdapServer = args[0]
tui.SetupApp()
},
}

rootCmd.Flags().IntVarP(&tui.LdapPort, "port", "P", 389, "LDAP server port")
rootCmd.Flags().StringVarP(&tui.LdapUsername, "username", "u", "", "LDAP username")
rootCmd.Flags().StringVarP(&tui.LdapPassword, "password", "p", "", "LDAP password")
rootCmd.Flags().StringVarP(&tui.LdapPasswordFile, "passfile", "", "", "Path to a file containing the LDAP password")
rootCmd.Flags().StringVarP(&tui.DomainName, "domain", "d", "", "Domain for NTLM / Kerberos authentication")
rootCmd.Flags().StringVarP(&tui.NtlmHash, "hashes", "H", "", "NTLM hash")
rootCmd.Flags().BoolVarP(&tui.Kerberos, "kerberos", "k", false, "Use Kerberos ticket for authentication (CCACHE specified via KRB5CCNAME environment variable)")
rootCmd.Flags().StringVarP(&tui.TargetSpn, "spn", "t", "", "Target SPN to use for Kerberos bind (usually ldap/dchostname)")
rootCmd.Flags().StringVarP(&tui.NtlmHashFile, "hashfile", "", "", "Path to a file containing the NTLM hash")
rootCmd.Flags().StringVarP(&tui.RootDN, "rootDN", "r", "", "Initial root DN")
rootCmd.Flags().StringVarP(&tui.SearchFilter, "filter", "f", "(objectClass=*)", "Initial LDAP search filter")
rootCmd.Flags().BoolVarP(&tui.Emojis, "emojis", "E", true, "Prefix objects with emojis")
rootCmd.Flags().BoolVarP(&tui.Colors, "colors", "C", true, "Colorize objects")
rootCmd.Flags().BoolVarP(&tui.FormatAttrs, "format", "F", true, "Format attributes into human-readable values")
rootCmd.Flags().BoolVarP(&tui.ExpandAttrs, "expand", "A", true, "Expand multi-value attributes")
rootCmd.Flags().IntVarP(&tui.AttrLimit, "limit", "L", 20, "Number of attribute values to render for multi-value attributes when -expand is set true")
rootCmd.Flags().BoolVarP(&tui.CacheEntries, "cache", "M", true, "Keep loaded entries in memory while the program is open and don't query them again")
rootCmd.Flags().BoolVarP(&tui.Deleted, "deleted", "D", false, "Include deleted objects in all queries performed")
rootCmd.Flags().Int32VarP(&tui.Timeout, "timeout", "T", 10, "Timeout for LDAP connections in seconds")
rootCmd.Flags().BoolVarP(&tui.LoadSchema, "schema", "s", false, "Load schema GUIDs from the LDAP server during initialization")
rootCmd.Flags().Uint32VarP(&tui.PagingSize, "paging", "G", 800, "Default paging size for regular queries")
rootCmd.Flags().BoolVarP(&tui.Insecure, "insecure", "I", false, "Skip TLS verification for LDAPS/StartTLS")
rootCmd.Flags().BoolVarP(&tui.Ldaps, "ldaps", "S", false, "Use LDAPS for initial connection")
rootCmd.Flags().StringVarP(&tui.SocksServer, "socks", "x", "", "Use a SOCKS proxy for initial connection")
rootCmd.Flags().StringVarP(&tui.KdcHost, "kdc", "", "", "Address of the KDC to use with Kerberos authentication (optional: only if the KDC differs from the specified LDAP server)")
rootCmd.Flags().StringVarP(&tui.TimeFormat, "timefmt", "", "", "Time format for LDAP timestamps")

versionCmd := &cobra.Command{
Use: "version",
Short: "Print the version number of the application",
DisableFlagsInUseLine: true,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(tui.GodapVer)
},
}

rootCmd.AddCommand(versionCmd)

if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
}
}
35 changes: 35 additions & 0 deletions pkg/adidns/colors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package adidns

func GetPropCellColor(propId uint32, cellValue string) (string, bool) {
switch cellValue {
case "Enabled":
return "green", true
case "Disabled", "None":
return "red", true
case "Unknown", "Not specified":
return "gray", true
}

switch propId {
case 0x00000001:
switch cellValue {
case "PRIMARY":
return "green", true
case "CACHE":
return "blue", true
}
case 0x00000002:
switch cellValue {
case "None":
return "red", true
case "Nonsecure and secure":
return "yellow", true
case "Secure only":
return "green", true
default:
return "gray", true
}
}

return "", false
}
107 changes: 107 additions & 0 deletions pkg/adidns/formats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package adidns

import (
"encoding/binary"
"fmt"
"math"
"net"
"time"
)

func ParseIP(data []byte) string {
ip := net.IP(data)
return ip.String()
}

func ParseAddrArray(data []byte) []string {
if len(data) == 0 {
return nil
}

numIPs := int(data[0])
if len(data) < 32*numIPs+32 {
return nil
}

addrArr := data[32:]

ips := make([]string, numIPs)
for x := 0; x < numIPs; x += 1 {
family := binary.LittleEndian.Uint16(addrArr[:4])

var ip net.IP
if family == 0x0002 {
// IPv4
ip = net.IP(addrArr[x*32+4 : x*32+8])
} else if family == 0x0017 {
// IPv6
ip = net.IP(addrArr[x*32+8 : x*32+24])
} else {
continue
}

ips[x] = ip.String()
}

return ips
}

func ParseIP4Array(data []byte) []string {
if len(data) == 0 {
return nil
}

numIP4s := int(data[0])
if len(data) < 4*numIP4s+1 {
return nil
}

ip4s := make([]string, numIP4s)
for x := 0; x < numIP4s; x += 1 {
ip := net.IP(data[1+x*4 : 1+(x+1)*4])
ip4s = append(ip4s, ip.String())
}

return ip4s
}

func FormatHours(val uint64) string {
days := 0
if val > 24 {
days = int(math.Floor(float64(val / 24)))
}

text := ""
if days > 0 {
text = fmt.Sprintf("%d days", days)
if val%24 != 0 {
text += fmt.Sprintf(", %d hours", val%24)
}
} else {
text = fmt.Sprintf("%d hours", val)
}

return text
}

// msTime is defined as the number of seconds since Jan 1th of 1601
// to calculate it we just compute a unix timestamp after
// removing the difference in seconds
// between 01/01/1601 and 01/01/1970
func MSTimeToUnixTimestamp(msTime uint64) int64 {
if msTime == 0 {
return -1
}

baseTime := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)

secondsSince := msTime - uint64(11644473600)

elapsedDuration := time.Duration(secondsSince) * time.Second

targetTime := baseTime.Add(elapsedDuration)

unixTimestamp := targetTime.Unix()

return unixTimestamp
}
Loading

0 comments on commit 128a27d

Please sign in to comment.