Selamlar Nasılsınız Umarım İyisinizdir BUGÜN Go (Golang) uygulamalarında SSL sertifika doğrulamasını nasıl atlayacağınızı göstereceğim. Bu görev genellikle güvenliği analiz ederken ve kaynak kodu mevcut olmadığında HTTPS isteklerine müdahale ederken ortaya çıkar. Sertifika denetimini devre dışı bırakmak için ikili dosyayı değiştirmenin ve aynı zamanda bir Python betiği kullanarak bu işlemi otomatikleştirmenin yollarına bakacağım.
Ağ kitaplıklarında ve HTTP isteklerini işlerken, programcı genellikle yapılandırmayı değiştirerek veya HTTP işleyicisine bayraklar ekleyerek SSL denetimini devre dışı bırakabilir. Bu durumda da işe yarayabileceğini düşündüm. Arama sırasında yapılandırmada varsayılan olarak false olarak ayarlanmış InsecureSkipVerify parametresini buldum . SSL doğrulamasını devre dışı bırakmak için uygulamanıza aşağıdaki kodu eklemeniz gerekiyordu:
Ancak önceden derlenmiş bir uygulamayla çalıştığım ve kaynak koduna erişimim olmadığı için bu yöntem uygun değildi. Farklı bir yaklaşım aramam gerekiyordu.
Bir sonraki adım, program ikili dosyasında InsecureSkipVerify bayrağının nerede kullanıldığını ve nasıl atlanabileceğini bulmaktı . Uygulamanın ikili dosya formatını ve derleme kodunu anlamaya çalışmak yerine net/http kütüphanesinin kaynak kodunu incelemeye karar verdim .
Go kodunda arama yaptığımda, InsecureSkipVerify bayrağının, validServerCertificate işlevindeki crypto/tls/handshake_client.go dosyasında kullanıldığını keşfettim :
Bu işlevi inceledikten sonra, işaret true olarak ayarlandığında sunucu sertifikası doğrulamasının atlandığı anlaşıldı. Böylece ilgili koşulu veya montaj kodunu değiştirerek sertifika doğrulamasını atlayabildim.
Gösterim amacıyla ipinfo için GET isteği oluşturan basit bir Go uygulaması yazdım . io'yu açar ve sonucu verir:
Çoğu zaman programcılar, dizeler ve işlev adları gibi gereksiz bilgileri gizlemek için hata ayıklama sembollerini uygulamalarından kaldırır. Ancak gösteri amacıyla, tersine mühendislik sürecini kolaylaştırmak için ikili dosyayı değiştirmeden bıraktım.
Assembly kodunu analiz etmeye başlamadan önce, validServerCertificate fonksiyonunun kaynak koduna bakalım . Kodu üç bölüme ayırır:
Amacım, programın SSL kontrolünü atlaması için koşulu değiştirmekti. Bu, jnz komutunu "sıfırsa atla" anlamına gelen jz olarak değiştirerek yapılabilir . X86 assembler'da bu değişiklik bir baytlık bir değişikliktir (85'ten 84'e).
Bu yamayı gerçekleştirmek için IDA PRO kullandım . İlgili kod parçasını IDA'da buldum ve 85 baytını 84 olarak değiştirdim, ardından değişiklikleri dosyaya uyguladım. Bundan sonra program SSL doğrulaması yapmayı durdurdu.
Süreci otomatikleştirmek için ikili dosyadaki cmp ve jnz talimatlarını arayan ve bunları jz ile değiştiren küçük bir Python betiği yazılmıştır . buda kod
Bu komut dosyası, SSL sertifikası doğrulamasını atlayarak derlenmiş Go ikili dosyalarını hızlı ve verimli bir şekilde değiştirmenize olanak tanır. Artık HTTPS isteklerini yakalamak ve Golang uygulamalarını analiz etmek çok daha kolay hale geliyor.
Kapanış
Bir, Sonraki Konuda Görüşmek Üzere.
Ağ kitaplıklarında ve HTTP isteklerini işlerken, programcı genellikle yapılandırmayı değiştirerek veya HTTP işleyicisine bayraklar ekleyerek SSL denetimini devre dışı bırakabilir. Bu durumda da işe yarayabileceğini düşündüm. Arama sırasında yapılandırmada varsayılan olarak false olarak ayarlanmış InsecureSkipVerify parametresini buldum . SSL doğrulamasını devre dışı bırakmak için uygulamanıza aşağıdaki kodu eklemeniz gerekiyordu:

Kod:
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
_, err := http.Get("https://golang.org/")
Kod:
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
_, err := client.Get("https://golang.org/")
Ancak önceden derlenmiş bir uygulamayla çalıştığım ve kaynak koduna erişimim olmadığı için bu yöntem uygun değildi. Farklı bir yaklaşım aramam gerekiyordu.
Bir sonraki adım, program ikili dosyasında InsecureSkipVerify bayrağının nerede kullanıldığını ve nasıl atlanabileceğini bulmaktı . Uygulamanın ikili dosya formatını ve derleme kodunu anlamaya çalışmak yerine net/http kütüphanesinin kaynak kodunu incelemeye karar verdim .
Go kodunda arama yaptığımda, InsecureSkipVerify bayrağının, validServerCertificate işlevindeki crypto/tls/handshake_client.go dosyasında kullanıldığını keşfettim :
Kod:
1
func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
// ...
if !c.config.InsecureSkipVerify {
opts := x509.VerifyOptions{
Roots: c.config.RootCAs,
CurrentTime: c.config.time(),
DNSName: c.config.ServerName,
Intermediates: x509.NewCertPool(),
}
// ...
}
// ...
}
Bu işlevi inceledikten sonra, işaret true olarak ayarlandığında sunucu sertifikası doğrulamasının atlandığı anlaşıldı. Böylece ilgili koşulu veya montaj kodunu değiştirerek sertifika doğrulamasını atlayabildim.
Gösterim amacıyla ipinfo için GET isteği oluşturan basit bir Go uygulaması yazdım . io'yu açar ve sonucu verir:
Kod:
package main
import (
"io/ioutil"
"log"
"net/http"
)
func main() {
resp, err := http.Get("https://ipinfo.io/")
if err != nil {
log.Fatalln(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
sb := string(body)
log.Printf(sb)
}
Çoğu zaman programcılar, dizeler ve işlev adları gibi gereksiz bilgileri gizlemek için hata ayıklama sembollerini uygulamalarından kaldırır. Ancak gösteri amacıyla, tersine mühendislik sürecini kolaylaştırmak için ikili dosyayı değiştirmeden bıraktım.
Assembly kodunu analiz etmeye başlamadan önce, validServerCertificate fonksiyonunun kaynak koduna bakalım . Kodu üç bölüme ayırır:
- Önbellekte sunucunun varlığının kontrol edilmesi.
- InsecureSkipVerify bayrağı kontrol ediliyor .
- Ortak anahtarın ve sertifikanın doğrulanması.
Amacım, programın SSL kontrolünü atlaması için koşulu değiştirmekti. Bu, jnz komutunu "sıfırsa atla" anlamına gelen jz olarak değiştirerek yapılabilir . X86 assembler'da bu değişiklik bir baytlık bir değişikliktir (85'ten 84'e).
Bu yamayı gerçekleştirmek için IDA PRO kullandım . İlgili kod parçasını IDA'da buldum ve 85 baytını 84 olarak değiştirdim, ardından değişiklikleri dosyaya uyguladım. Bundan sonra program SSL doğrulaması yapmayı durdurdu.


Süreci otomatikleştirmek için ikili dosyadaki cmp ve jnz talimatlarını arayan ve bunları jz ile değiştiren küçük bir Python betiği yazılmıştır . buda kod

Kod:
#!/usr/bin/env python3
import subprocess
import argparse
supported_versions_to_bytes = {
'11': [b"\x00\x0F\x85\xB3\x04\x00\x00", b"\x00\x0F\x84\xB3\x04\x00\x00"],
'12': [b"\x00\x00\x0F\x85\x43\x05\x00\x00", b"\x00\x00\x0F\x84\x43\x05\x00\x00"],
'13': [b"\x00\x00\x0F\x85\x32\x05\x00\x00", b"\x00\x00\x0F\x84\x32\x05\x00\x00"],
'14': [b"\x00\x00\x0F\x85\x48\x05\x00\x00", b"\x00\x00\x0F\x84\x48\x05\x00\x00"],
'15': [b"\x00\x00\x0F\x85\x3A\x06\x00\x00", b"\x00\x00\x0F\x84\x3A\x06\x00\x00"],
'16': [b"\x00\x00\x0F\x85\x5A\x06\x00\x00", b"\x00\x00\x0F\x84\x5A\x06\x00\x00"],
'17': [b"\x00\x00\x0F\x85\x7F\x01\x00\x00", b"\x00\x00\x0F\x84\x7F\x01\x00\x00"],
'18': [b"\x00\x00\x0F\x85\x7C\x01\x00\x00", b"\x00\x00\x0f\x84\x7C\x01\x00\x00"],
'19': [b"\x00\x00\x0F\x85\x7B\x01\x00\x00", b"\x00\x00\x0f\x84\x7B\x01\x00\x00"],
'20': [b"\x00\x00\x0F\x85\x84\x01\x00\x00", b"\x00\x00\x0F\x84\x84\x01\x00\x00"],
'21': [b"\x00\x00\x0F\x85\x82\x01\x00\x00", b"\x00\x00\x0F\x84\x82\x01\x00\x00"]
}
def replace_file_bytes(file_path, old_bytes, new_bytes):
with open(file_path, 'rb') as f:
data = f.read()
position = data.find(old_bytes)
if(-1 == position):
raise Exception("cannot find bytes, maybe the program is already patched?")
with open(file_path, 'rb+') as file:
file.seek(position)
existing_bytes = file.read(len(old_bytes))
if existing_bytes == old_bytes:
file.seek(position)
file.write(new_bytes)
def run_command(command):
result = subprocess.run(command, shell=True, capture_output=True, text=True)
return result.stdout.strip()
def get_go_bin_version(filename):
output = run_command(f"strings {filename} | grep '^go1' | head -n 1")
if "" == output:
output = run_command(f"strings {filename} | grep 'Go cmd/compile' | head -n 1 | cut -d' ' -f 3")
if "" == output:
raise Exception("cannot determine go binary version")
return output.replace("go", "").replace(".", "")
def patch_file(filename, version):
if version not in supported_versions_to_bytes:
raise Exception("unsupported version to patch. supported versions are: " + str(supported_versions_to_bytes.keys()))
bytes_arr = supported_versions_to_bytes[version]
replace_file_bytes(filename, bytes_arr[0], bytes_arr[1])
def main():
parser = argparse.ArgumentParser(description="Patching Go binaries to ignore SSL")
parser.add_argument('binary', type=str, help='Go binary to patch')
args = parser.parse_args()
bin_version = get_go_bin_version(args.binary)
print(f"Found version: {bin_version}")
patch_file(args.binary, bin_version)
if __name__ == "__main__":
main()

Bu komut dosyası, SSL sertifikası doğrulamasını atlayarak derlenmiş Go ikili dosyalarını hızlı ve verimli bir şekilde değiştirmenize olanak tanır. Artık HTTPS isteklerini yakalamak ve Golang uygulamalarını analiz etmek çok daha kolay hale geliyor.
Kapanış
Bir, Sonraki Konuda Görüşmek Üzere.