Chocapikk/pgread
GitHub: Chocapikk/pgread
无需数据库凭证即可直接读取 PostgreSQL 物理数据文件,支持数据提取、取证分析及安全研究的 Go 语言工具。
Stars: 45 | Forks: 2
# pgread
[](https://github.com/Chocapikk/pgread/actions/workflows/test.yml)
[](https://codecov.io/gh/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分析, 删除数据恢复, 哈希提取, 敏感数据扫描, 数据库取证, 数据恢复, 数据泄露, 数据转储, 文件解析, 无凭证读取, 日志审计, 电子取证, 程序破解, 系统目录