Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support UTF-8 overlong encoding #11

Merged
merged 7 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/setup-go@v2
with:
go-version: '1.17'
go-version: '1.22'

- uses: actions/checkout@v2
- uses: golangci/golangci-lint-action@v2
Expand All @@ -24,7 +24,7 @@ jobs:
needs: [ lint ]
strategy:
matrix:
go-version: [ 1.15.x, 1.16.x, 1.17.x ]
go-version: [ 1.22.x, 1.21.x, 1.20.x, 1.19.x, 1.18.x ]
os: [ ubuntu-latest, macos-latest, windows-latest ]
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -50,7 +50,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-go@v2
with:
go-version: 1.17
go-version: 1.22
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
Expand Down
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,25 @@ Next, use `github.com/phith0n/zkar/*` in your application:
package main

import (
"fmt"
"github.com/phith0n/zkar/serz"
"io/ioutil"
"log"
"fmt"
"github.com/phith0n/zkar/serz"
"log"
"os"
)

func main() {
data, _ := ioutil.ReadFile("./testcases/ysoserial/CommonsCollections6.ser")
serialization, err := serz.FromBytes(data)
if err != nil {
log.Fatal("parse error")
}
data, _ := os.ReadFile("./testcases/ysoserial/CommonsCollections6.ser")
serialization, err := serz.FromBytes(data)
if err != nil {
log.Fatal("parse error")
}

fmt.Println(serialization.ToString())
fmt.Println(serialization.ToString())
}
```

[Here](serz/tc_utf_test.go) is an example to show how to read an exist payload and modify it to a UTF-8 overlong encoding payload.

## 💻 Command line utility tool

ZKar also provides a command line utility tool that you can use it directly:
Expand Down Expand Up @@ -125,6 +127,7 @@ As the payload is not a valid serialized data stream, it's necessary to tell ZKa
- [x] JDK/JRE 8u20 Gadget supporting
- [ ] Serialization payloads generator
- [ ] An implementation of RMI/LDAP in Go
- [x] Support read/write UTF-8 overlong encoding feature

## ⚖️ License

Expand Down
21 changes: 14 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
module github.com/phith0n/zkar

go 1.16
go 1.18

require (
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/phith0n/litter v1.5.3
github.com/stretchr/testify v1.7.0
github.com/thoas/go-funk v0.9.0
github.com/urfave/cli/v2 v2.3.0
github.com/stretchr/testify v1.9.0
github.com/thoas/go-funk v0.9.3
github.com/urfave/cli/v2 v2.27.1
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
32 changes: 14 additions & 18 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -14,24 +12,22 @@ github.com/phith0n/litter v1.5.3 h1:HJBgdlibJT57lmkT6XZPeWQCuDGKf9QkKHgnJJ98+io=
github.com/phith0n/litter v1.5.3/go.mod h1:T2hptLevnpc+lnUskkc/7yF6wvuej1A6fuMfP2uGbZ0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/thoas/go-funk v0.9.0 h1:Yzu8aTjTb1sqHZzSZLBt4qaZrFfjNizhA7IfnefjEzo=
github.com/thoas/go-funk v0.9.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw=
github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
79 changes: 71 additions & 8 deletions serz/tc_utf.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,35 @@ import (
"github.com/phith0n/zkar/commons"
)

type OverlongOption int

const (
OverlongEncodingTwoBytes OverlongOption = 2
OverlongEncodingThreeBytes OverlongOption = 3
)

type TCUtf struct {
Data string

OverlongSize OverlongOption
}

func (u *TCUtf) ToBytes() []byte {
var bs []byte
var length = len(u.Data)
if length <= 0xFFFF {
bs = commons.NumberToBytes(uint16(len(u.Data)))
var data []byte
if u.OverlongSize == OverlongEncodingTwoBytes || u.OverlongSize == OverlongEncodingThreeBytes {
data = toOverlongEncoding([]byte(u.Data), u.OverlongSize)
} else {
bs = commons.NumberToBytes(uint64(len(u.Data)))
data = []byte(u.Data)
}

var length []byte
if len(data) <= 0xFFFF {
length = commons.NumberToBytes(uint16(len(data)))
} else {
length = commons.NumberToBytes(uint64(len(data)))
}

return append(bs, []byte(u.Data)...)
return append(length, data...)
}

func (u *TCUtf) ToString() string {
Expand All @@ -41,6 +56,10 @@ func (u *TCUtf) Walk(callback WalkCallback) error {
return nil
}

func (u *TCUtf) SetOverlongSize(size OverlongOption) {
u.OverlongSize = size
}

func readUTF(stream *ObjectStream) (*TCUtf, error) {
var bs []byte
var err error
Expand All @@ -59,7 +78,7 @@ func readUTF(stream *ObjectStream) (*TCUtf, error) {
}

return &TCUtf{
Data: string(data),
Data: string(fromOverlongEncoding(data)),
}, nil
}

Expand All @@ -81,6 +100,50 @@ func readLongUTF(stream *ObjectStream) (*TCUtf, error) {
}

return &TCUtf{
Data: string(data),
Data: string(fromOverlongEncoding(data)),
}, nil
}

func toOverlongEncoding(data []byte, size OverlongOption) []byte {
var bs []byte
for _, ch := range data {
if size == OverlongEncodingTwoBytes {
bs = append(bs, ((ch>>6)&0b11111)|0b11000000)
bs = append(bs, (ch&0b111111)|0b10000000)
} else {
bs = append(bs, 0b11100000)
bs = append(bs, (ch>>6&0b111111)|0b10000000)
bs = append(bs, (ch&0b111111)|0b10000000)
}
}

return bs
}

func fromOverlongEncoding(data []byte) []byte {
var rs []byte
for i := 0; i < len(data); {
b1 := data[i]
i++
if i < len(data) && b1>>5 == 0b110 {
b2 := data[i]
if b1>>1 == 0b1100000 && b2>>6 == 0b10 {
rs = append(rs, (b2&0b111111)|(b1<<6))
i++
continue
}
} else if i+1 < len(data) && b1>>4 == 0b1110 {
b2 := data[i]
b3 := data[i+1]

if b1 == 0b11100000 && b2>>1 == 0b1000000 && b3>>6 == 0b10 {
rs = append(rs, (b3&0b111111)|(b2<<6))
i += 2
continue
}
}

rs = append(rs, b1)
}
return rs
}
51 changes: 51 additions & 0 deletions serz/tc_utf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package serz

import (
"os"
"testing"

"github.com/stretchr/testify/require"
)

// TestCC6WithOverlongEncoding show how to read an exist payload and modify it to a UTF-8 overlong encoding payload.
func TestCC6WithOverlongEncoding(t *testing.T) {
// read original CommonsCollections6 payload
data, err := os.ReadFile("../testcases/ysoserial/CommonsCollections6.ser")
require.NoError(t, err)
ser, err := FromBytes(data)
require.NoError(t, err)

// enable UTF-8 overlong encoding feature
err = ser.Walk(func(obj Object) error {
if u, ok := obj.(*TCUtf); ok {
u.SetOverlongSize(OverlongEncodingTwoBytes)
}

return nil
})
require.NoError(t, err)

// generate new payload
data2 := ser.ToBytes()

// read new payload
_, err = FromBytes(data2)
require.NoError(t, err)
}

func TestToOverlongEncoding(t *testing.T) {
require.Equal(t, []byte("\xC0\xAE"), toOverlongEncoding([]byte("."), OverlongEncodingTwoBytes))
require.Equal(t, []byte("\xE0\x80\xAE"), toOverlongEncoding([]byte("."), OverlongEncodingThreeBytes))
require.Equal(t, []byte("\xc1\xaf\xc1\xb2\xc1\xa7\xc0\xae\xc1\xa5\xc1\xb8\xc1\xa1\xc1\xad\xc1\xb0\xc1"+
"\xac\xc1\xa5\xc0\xae\xc1\x85\xc1\xb6\xc1\xa9\xc1\xac"),
toOverlongEncoding([]byte("org.example.Evil"), OverlongEncodingTwoBytes))

require.Equal(t, []byte("."), fromOverlongEncoding(toOverlongEncoding([]byte("."), OverlongEncodingTwoBytes)))
require.Equal(t, []byte("."), fromOverlongEncoding(toOverlongEncoding([]byte("."), OverlongEncodingThreeBytes)))
require.Equal(t, []byte("org.example.Evil"), fromOverlongEncoding(toOverlongEncoding([]byte("org.example.Evil"), OverlongEncodingTwoBytes)))
require.Equal(t, []byte("org.example.Evil"), fromOverlongEncoding(toOverlongEncoding([]byte("org.example.Evil"), OverlongEncodingThreeBytes)))

require.Equal(t, []byte{0xC0, 0xFF}, fromOverlongEncoding([]byte{0xC0, 0xFF}))
require.Equal(t, []byte{0xE0, 0xAE, 0x38}, fromOverlongEncoding([]byte{0xE0, 0xAE, 0x38}))
require.Equal(t, []byte("org.example.Evil"), fromOverlongEncoding([]byte("org.example.Evil")))
}
Loading