Chocapikk/pgread

GitHub: Chocapikk/pgread

无需数据库凭证即可直接读取 PostgreSQL 物理数据文件,支持数据提取、取证分析及安全研究的 Go 语言工具。

Stars: 45 | Forks: 2

# pgread [![Test](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/763a4c9e73215244.svg)](https://github.com/Chocapikk/pgread/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/Chocapikk/pgread/branch/main/graph/badge.svg)](https://codecov.io/gh/Chocapikk/pgread) [![Go Report Card](https://goreportcard.com/badge/github.com/Chocapikk/pgread)](https://goreportcard.com/report/github.com/Chocapikk/pgread) 无需凭证导出 PostgreSQL 数据 - 如果你能读取文件,你就可以导出数据库。 ## 技术原理 PostgreSQL 为系统目录使用 **固定 OID**: | OID | 目录 | 路径 | |-----|---------|------| | 1262 | pg_database | `global/1262` | | 1259 | pg_class | `base//1259` | | 1249 | pg_attribute | `base//1249` | 泄露这 3 个文件 → 发现整个模式 → 导出任何表。 ## 安装 ``` go install github.com/Chocapikk/pgread@latest ``` ## CLI ``` # 基本用法 pgread # Auto-detect and dump (JSON) pgread -table # Output as psql-style table pgread -sql # Output as SQL statements pgread -csv # Output as CSV pgread -sql -db mydb > backup.sql # Export to SQL file pgread -d /path/to/data/ # Specify data directory pgread -d /path/to/data/ -db mydb # Specific database pgread -d /path/to/data/ -t password # Filter tables pgread -d /path/to/data/ -list # Schema only pgread -f /path/to/1262 # Parse single file # 安全 / 取证 pgread -passwords all # Extract ALL password hashes pgread -passwords postgres # Extract specific user's hash pgread -secrets auto # Auto-detect secrets (API keys, etc) pgread -search "password|secret" # Search with regex pgread -deleted # Include deleted rows (forensics) pgread -wal # WAL transaction summary pgread -detect # Show detected PostgreSQL paths # 底层 / 取证 pgread -control # pg_control file (version, state, LSN) pgread -checksum # Verify page checksums (corruption) pgread -dropped # Show dropped columns (recoverable) pgread -sequences all # List all sequences with values pgread -relmap global # Show pg_filenode.map (OID→filenode) pgread -f /path/to/file -R 0:10 # Read specific block range pgread -f /path/to/index -index # Parse index file (BTree/GIN/GiST/Hash) pgread -encoding GBK -sql # Output in GBK encoding (auto-detects DB encoding) ``` ### 密码提取 ``` $ pgread -passwords all PostgreSQL Password Hashes: =========================== postgres:SCRAM-SHA-256$4096:salt$hash:proof [SUPERUSER] [LOGIN] admin:SCRAM-SHA-256$4096:salt$hash:proof [LOGIN] ``` ### 密钥检测 (由 Trufflehog 提供支持) 使用 [trufflehog](https://github.com/trufflesecurity/trufflehog) 的 700+ 种检测器: ``` $ pgread -secrets auto [ { "detector": "Stripe", "database": "postgres", "table": "api_keys", "column": "value", "raw": "sk_live_51Hx...", "extra_data": { "rotation_guide": "https://howtorotate.com/docs/tutorials/stripe/" } } ] ``` 检测对象:Stripe、AWS、GitHub、GitLab、Slack、SendGrid、Doppler、DigitalOcean、Heroku 以及 700 多种其他密钥。 ### WAL 分析 ``` $ pgread -wal { "segment_count": 1, "record_count": 24574, "pg_version": "16", "operations": { "INSERT": 4440, "DELETE": 106, "UPDATE": 379, "COMMIT": 738, ... }, "transactions": [...] } ``` ### pg_control 解析 ``` $ pgread -control { "pg_control_version": 1300, "catalog_version_no": 202307071, "system_identifier": 7123456789012345678, "state": 6, "state_string": "IN_PRODUCTION", "checkpoint_lsn": 4294967376, "checkpoint_lsn_str": "0/100000050", "pg_version_major": 16, "data_checksums_enabled": true, ... } ``` ### 校验和验证 ``` $ pgread -checksum { "data_dir": "/var/lib/postgresql/data", "checksums_enabled": true, "total_files": 42, "total_blocks": 1024, "valid_blocks": 1024, "invalid_blocks": 0 } ``` 在 PostgreSQL 之前检测到页面损坏! ### 索引解析 ``` $ pgread -f /var/lib/postgresql/data/base/16384/16385 -index { "type": 1, "type_string": "btree", "total_pages": 5, "root_page": 1, "levels": 2, "meta": { "magic": 340322, "version": 4, "root": 1, "level": 2 }, "pages": [...] } ``` 支持:**BTree**、**GIN**、**GiST**、**Hash**、**SP-GiST** ### 已删除列恢复 ``` $ pgread -dropped [ { "database": "mydb", "dropped_count": 2, "columns": [ { "rel_oid": 16384, "table_name": "users", "attnum": 3, "dropped_name": "........pg.dropped.3........", "type_oid": 25, "type_name": "text" } ] } ] ``` 从执行了 `ALTER TABLE DROP COLUMN` 的列中恢复数据! ### 序列解析 ``` $ pgread -sequences mydb [ { "name": "users_id_seq", "oid": 16396, "filenode": 16396, "last_value": 42, "start_value": 1, "increment_by": 1, "max_value": 9223372036854775807, "min_value": 1, "is_cycled": false, "is_called": true } ] ``` ### pg_filenode.map 解析 ``` $ pgread -relmap global { "magic": 5842711, "num_mappings": 50, "mappings": [ {"oid": 1262, "filenode": 1262}, {"oid": 1260, "filenode": 1260}, ... ] } ``` 将系统目录 OID 映射到它们的物理文件节点。 ### 块范围选择 ``` $ pgread -f /path/to/heap -R 0:5 [ { "block_number": 0, "lsn": "0/19921E0", "checksum": 0, "lower": 212, "upper": 7744, "page_size": 8192, "item_count": 47, "free_space": 7532 }, ... ] ``` 读取指定块:`0:10`(块 0-10)、`5:`(从 5 开始)、`:20`(到 20 结束)、`5`(仅块 5)。 ### 二进制块转储 ``` $ pgread -f /path/to/heap -b -R 0 Block 0 (offset 0x00000000): 00000000 00 00 00 00 40 2e 4f 01 00 00 01 00 30 00 20 1d |....@.O.....0. .| 00000010 00 20 04 20 00 00 00 00 05 00 01 00 06 00 01 00 |. . ............| ... ``` 类似 `xxd` 或 `hexdump -C` 的原始十六进制转储。适用于底层取证。 ### 多段文件 PostgreSQL 将大表拆分为 1GB 的段。pgread 会自动处理: ``` # 从特定 segment 读取 $ pgread -f /path/to/16384.2 -n 2 -R 0:10 # 自定义 segment 大小(例如,某些配置为 128MB) $ pgread -f /path/to/file -s 134217728 -R 0:100 ``` ### TOAST 详细信息 ``` $ pgread -f /path/to/toast_table -toast-verbose { "toast_rel_id": 16385, "total_chunks": 150, "unique_values": 42, "total_size": 1048576, "average_chunk_size": 6990.5, "max_chunks_per_value": 12, "chunk_distribution": {"1": 20, "2": 15, "5": 5, "12": 2} } ``` ### 表格输出 (psql 风格) ``` $ pgread -d /path/to/data -db mydb -t users -table mydb.users (3 rows) email | password_hash | is_admin --------------------+-----------------------------------------------+--------- admin@example.com | $argon2id$v=19$m=19456,t=2,p=1$salt$hash | true alice@example.com | $argon2id$v=19$m=19456,t=2,p=1$salt$hash | false bob@example.com | $argon2id$v=19$m=19456,t=2,p=1$salt$hash | false (3 rows) ``` ### SQL/CSV 导出 ``` # 导出整个数据库 pgread -sql -db mydb > mydb_backup.sql psql -d newdb < mydb_backup.sql # 导出为 CSV pgread -csv -db mydb > mydb.csv ``` ## 库 ``` import "github.com/Chocapikk/pgread/pgdump" // Auto-detect and dump ALL PostgreSQL instances results, _ := pgdump.DumpAll(nil) // Or specify a path result, _ := pgdump.DumpDataDir("/var/lib/postgresql/data", nil) // With options result, _ := pgdump.DumpDataDir("/path/to/data", &pgdump.Options{ DatabaseFilter: "mydb", TableFilter: "password", }) // Custom file reader (arbitrary file read, SSRF, backups, etc.) pgdump.DumpDatabaseFromFiles(classData, attrData, func(fn uint32) ([]byte, error) { return httpClient.Get(fmt.Sprintf("/base/%d/%d", dbOID, fn)) }, nil) // Export to SQL result, _ := pgdump.DumpDataDir("/path/to/data", nil) result.ToSQL(os.Stdout) // or any io.Writer ``` ### 自动检测 ``` // Find first PostgreSQL data directory dataDir := pgdump.DetectDataDir() // Find ALL PostgreSQL data directories dataDirs := pgdump.DetectAllDataDirs() ``` ### 底层 API ``` // Parse system catalogs databases := pgdump.ParsePGDatabase(data) // []DatabaseInfo tables := pgdump.ParsePGClass(data) // map[filenode]TableInfo columns := pgdump.ParsePGAttribute(data,0) // map[oid][]AttrInfo // Decode table data rows := pgdump.ReadRows(tableData, schema, true) // Raw tuple access tuples := pgdump.ReadTuples(data, true) row := pgdump.DecodeTuple(tuple, columns) // pg_control parsing control, _ := pgdump.ReadControlFile(dataDir) fmt.Printf("PG Version: %d, State: %s\n", control.PGVersionMajor, control.StateString) // Checksum verification result, _ := pgdump.VerifyDataDirChecksums(dataDir) fmt.Printf("Valid: %d, Invalid: %d\n", result.ValidBlocks, result.InvalidBlocks) // Index parsing indexInfo, _ := pgdump.ParseIndexFile(data) fmt.Printf("Type: %s, Root: %d\n", indexInfo.TypeString, indexInfo.RootPage) // Dropped columns dropped, _ := pgdump.FindDroppedColumns(dataDir, "mydb") for _, col := range dropped.Columns { fmt.Printf("Dropped: %s.%d (%s)\n", col.TableName, col.AttNum, col.TypeName) } ``` ## 支持的类型 **数值型:** `bool` `int2` `int4` `int8` `float4` `float8` `numeric` `money` **文本型:** `text` `varchar` `char` `bpchar` `name` `bytea` **日期/时间:** `date` `time` `timetz` `timestamp` `timestamptz` `interval` **网络:** `inet` `cidr` `macaddr` `macaddr8` **几何:** `point` `line` `lseg` `box` `circle` `path` `polygon` **结构化:** `json` `jsonb` `jsonpath` `xml` `uuid` **范围:** `int4range` `int8range` `numrange` `daterange` `tsrange` `tstzrange` **文本搜索:** `tsvector` `tsquery` **其他:** `oid` `tid` `xid` `cid` `pg_lsn` `bit` `varbit` + **上述所有类型的数组** ## 构建 ``` go build GOOS=windows go build -o pgread.exe GOOS=darwin GOARCH=arm64 go build -o pgread-macos ``` ## 编码支持 pgread 会自动从 `pg_database` 检测数据库编码,默认转换为 UTF-8。使用 `-encoding` 输出特定字符集: ``` pgread -sql -encoding GBK > dump.sql # Output in GBK pgread -sql # Default: UTF-8 ``` 支持:UTF-8、GBK、GB18030、BIG5、SJIS、EUC-JP、EUC-KR、EUC-CN、LATIN1-5、WIN1250-1258、KOI8-R、KOI8-U、ISO-8859-5/6/7/8。 ## 已知限制 - **TOAST 与压缩:** 存储在 TOAST 表中的大值会自动解析,包括在 `VACUUM FULL` 之后。支持 **PGLZ** 和 **LZ4** 压缩,包括内联(保留在主堆中的小压缩值)和外部(在 TOAST 表中分块)。适用于 PostgreSQL 12-17。 - **加密数据:** 应用层加密的数据会原样返回(密文)。pgread 提取 PostgreSQL 存储的内容。 - **飞行中数据:** 最近写入仍位于共享缓冲区中的数据可能尚未写入磁盘。如果可能,请先运行 `CHECKPOINT`。 ## 相关 - [Windfall](https://github.com/Chocapikk/Windfall) - 利用该技术针对 Windmill/Nextcloud Flow RCE 的漏洞利用工具包 - [博客文章:无需凭证导出 PostgreSQL](https://chocapikk.com/posts/2026/dumping-postgresql-without-credentials/) - [博客文章:Windfall - 从路径遍历到 RCE](https://chocapikk.com/posts/2026/windfall-nextcloud-flow-windmill-rce/) ## 许可证 [WTFPL](http://www.wtfpl.net/) - Do What The Fuck You Want To Public License
标签:EVTX分析, Go语言, HTTP工具, OID, PostgreSQL, StruQ, WAL分析, 删除数据恢复, 哈希提取, 敏感数据扫描, 数据库取证, 数据恢复, 数据泄露, 数据转储, 文件解析, 无凭证读取, 日志审计, 电子取证, 程序破解, 系统目录